<commit_before>// exaplains zookeeper session expires mechanism.
// The code is extracted from zookeeper server code.
//
// Session expiration is managed by the ZooKeeper cluster itself, not by the client.
//
// When the ZK client establishes a session with the cluster it provides a "timeout" value.
// This value is used by the cluster to determine when the client's session expires.
//
// Expirations happens when the cluster does not hear from the client within the
// specified session timeout period (i.e. no heartbeat).
//
// At session expiration the cluster will delete any/all ephemeral nodes owned by that session and
// immediately notify any/all connected clients of the change (anyone watching those znodes).
// At this point the client of the expired session is still disconnected from the cluster, it
// will not be notified of the session expiration until/unless it is able to re-establish a
// connection to the cluster.
// The client will stay in disconnected state until the TCP connection is re-established with the
// cluster, at which point the watcher of the expired session will receive the "session expired"
// notification.

package main

import (
	"flag"
	"fmt"
)

var (
	tickTime          = 3000 // zoo.cfg, default 3s
	minSessionTimeout = -1   // zoo.cfg
	maxSessionTimeout = -1   // zoo.cfg
)

func getMinSessionTimeout() int {
	if minSessionTimeout == -1 {
		return tickTime * 2
	} else {
		return minSessionTimeout
	}
}

func getMaxSessionTimeout() int {
	if maxSessionTimeout == -1 {
		return tickTime * 20
	} else {
		return maxSessionTimeout
	}
}

func getRealSessionTimeout(clientSessionTimeout int) int {
	var sessionTimeout = clientSessionTimeout
	min, max := getMinSessionTimeout(), getMaxSessionTimeout()
	if sessionTimeout < min {
		sessionTimeout = min
	}
	if sessionTimeout > max {
		sessionTimeout = max
	}

	return sessionTimeout
}

func main() {
	var clientConfiggedSessionTimeout int
	flag.IntVar(&clientConfiggedSessionTimeout, "c", 30*1000, "client side zk session timeout, which will be sent to zk for negotiation")
	flag.IntVar(&tickTime, "t", 3000, "tickTime in zoo.cfg")
	flag.Parse()

	fmt.Printf("tickTime=%ds\n", tickTime/1000)
	fmt.Printf("default between: %ds ~ %ds\n",
		getRealSessionTimeout(-1)/1000,
		getRealSessionTimeout(1<<30)/1000)

	fmt.Printf("client sent:%ds => got %ds\n",
		clientConfiggedSessionTimeout/1000,
		getRealSessionTimeout(clientConfiggedSessionTimeout)/1000)
}
<commit_msg>update default tickTime to our production env<commit_after>// exaplains zookeeper session expires mechanism.
// The code is extracted from zookeeper server code.
//
// Session expiration is managed by the ZooKeeper cluster itself, not by the client.
//
// When the ZK client establishes a session with the cluster it provides a "timeout" value.
// This value is used by the cluster to determine when the client's session expires.
//
// Expirations happens when the cluster does not hear from the client within the
// specified session timeout period (i.e. no heartbeat).
//
// At session expiration the cluster will delete any/all ephemeral nodes owned by that session and
// immediately notify any/all connected clients of the change (anyone watching those znodes).
// At this point the client of the expired session is still disconnected from the cluster, it
// will not be notified of the session expiration until/unless it is able to re-establish a
// connection to the cluster.
// The client will stay in disconnected state until the TCP connection is re-established with the
// cluster, at which point the watcher of the expired session will receive the "session expired"
// notification.

package main

import (
	"flag"
	"fmt"
)

var (
	tickTime          = 2000 // zoo.cfg
	minSessionTimeout = -1   // zoo.cfg
	maxSessionTimeout = -1   // zoo.cfg
)

func getMinSessionTimeout() int {
	if minSessionTimeout == -1 {
		return tickTime * 2
	} else {
		return minSessionTimeout
	}
}

func getMaxSessionTimeout() int {
	if maxSessionTimeout == -1 {
		return tickTime * 20
	} else {
		return maxSessionTimeout
	}
}

func getRealSessionTimeout(clientSessionTimeout int) int {
	var sessionTimeout = clientSessionTimeout
	min, max := getMinSessionTimeout(), getMaxSessionTimeout()
	if sessionTimeout < min {
		sessionTimeout = min
	}
	if sessionTimeout > max {
		sessionTimeout = max
	}

	return sessionTimeout
}

func main() {
	var clientConfiggedSessionTimeout int
	flag.IntVar(&clientConfiggedSessionTimeout, "c", 30*1000, "client side zk session timeout, which will be sent to zk for negotiation")
	flag.IntVar(&tickTime, "t", 2000, "tickTime in zoo.cfg")
	flag.Parse()

	fmt.Printf("tickTime=%ds\n", tickTime/1000)
	fmt.Printf("default between: %ds ~ %ds\n",
		getRealSessionTimeout(-1)/1000,
		getRealSessionTimeout(1<<30)/1000)

	fmt.Printf("client sent:%ds => got %ds\n",
		clientConfiggedSessionTimeout/1000,
		getRealSessionTimeout(clientConfiggedSessionTimeout)/1000)
}
<|endoftext|>


<commit_before>package models

import (
	"errors"
	"github.com/stretchr/testify/assert"
	"gopkg.in/DATA-DOG/go-sqlmock.v1"
	"testing"

	"github.com/eirka/eirka-libs/db"
	e "github.com/eirka/eirka-libs/errors"
)

func TestAddTagIsValid(t *testing.T) {

	var err error

	tag := AddTagModel{
		Ib:    0,
		Tag:   1,
		Image: 1,
	}

	err = tag.Status()
	if assert.Error(t, err, "An error was not expected") {
		assert.Equal(t, err, errors.New("AddTagModel is not valid"), "Error should match")
	}

}

func TestAddTagStatus(t *testing.T) {

	var err error

	mock, err := db.NewTestDb()
	assert.NoError(t, err, "An error was not expected")

	statusrows := sqlmock.NewRows([]string{"count"}).AddRow(1)
	mock.ExpectQuery(`SELECT count\(1\) FROM images`).WillReturnRows(statusrows)

	duperows := sqlmock.NewRows([]string{"count"}).AddRow(0)
	mock.ExpectQuery(`select count\(1\) from tagmap`).WillReturnRows(duperows)

	tag := AddTagModel{
		Ib:    1,
		Tag:   1,
		Image: 1,
	}

	err = tag.Status()
	assert.NoError(t, err, "An error was not expected")

}

func TestAddTagStatusNotFound(t *testing.T) {

	var err error

	mock, err := db.NewTestDb()
	assert.NoError(t, err, "An error was not expected")

	statusrows := sqlmock.NewRows([]string{"count"}).AddRow(0)
	mock.ExpectQuery(`SELECT count\(1\) FROM images`).WillReturnRows(statusrows)

	duperows := sqlmock.NewRows([]string{"count"}).AddRow(0)
	mock.ExpectQuery(`select count\(1\) from tagmap`).WillReturnRows(duperows)

	tag := AddTagModel{
		Ib:    1,
		Tag:   1,
		Image: 1,
	}

	err = tag.Status()
	if assert.Error(t, err, "An error was not expected") {
		assert.Equal(t, err, e.ErrNotFound, "Error should match")
	}

}

func TestAddTagStatusDuplicate(t *testing.T) {

	var err error

	mock, err := db.NewTestDb()
	assert.NoError(t, err, "An error was not expected")

	statusrows := sqlmock.NewRows([]string{"count"}).AddRow(1)
	mock.ExpectQuery(`SELECT count\(1\) FROM images`).WillReturnRows(statusrows)

	duperows := sqlmock.NewRows([]string{"count"}).AddRow(1)
	mock.ExpectQuery(`select count\(1\) from tagmap`).WillReturnRows(duperows)

	tag := AddTagModel{
		Ib:    1,
		Tag:   1,
		Image: 1,
	}

	err = tag.Status()
	if assert.Error(t, err, "An error was not expected") {
		assert.Equal(t, err, e.ErrDuplicateTag, "Error should match")
	}

}

func TestAddTagPost(t *testing.T) {

	var err error

	mock, err := db.NewTestDb()
	assert.NoError(t, err, "An error was not expected")

	mock.ExpectExec("INSERT into tagmap").
		WithArgs(1, 1).
		WillReturnResult(sqlmock.NewResult(1, 1))

	tag := AddTagModel{
		Ib:    1,
		Tag:   1,
		Image: 1,
	}

	err = tag.Post()
	assert.NoError(t, err, "An error was not expected")

}
<commit_msg>add model tests<commit_after>package models

import (
	"errors"
	"github.com/stretchr/testify/assert"
	"gopkg.in/DATA-DOG/go-sqlmock.v1"
	"testing"

	"github.com/eirka/eirka-libs/db"
	e "github.com/eirka/eirka-libs/errors"
)

func TestAddTagIsValid(t *testing.T) {

	var err error

	tag := AddTagModel{
		Ib:    0,
		Tag:   1,
		Image: 1,
	}

	assert.False(t, tag.IsValid(), "Should be false")

}

func TestAddTagStatus(t *testing.T) {

	var err error

	mock, err := db.NewTestDb()
	assert.NoError(t, err, "An error was not expected")

	statusrows := sqlmock.NewRows([]string{"count"}).AddRow(1)
	mock.ExpectQuery(`SELECT count\(1\) FROM images`).WillReturnRows(statusrows)

	duperows := sqlmock.NewRows([]string{"count"}).AddRow(0)
	mock.ExpectQuery(`select count\(1\) from tagmap`).WillReturnRows(duperows)

	tag := AddTagModel{
		Ib:    1,
		Tag:   1,
		Image: 1,
	}

	err = tag.Status()
	assert.NoError(t, err, "An error was not expected")

}

func TestAddTagStatusNotFound(t *testing.T) {

	var err error

	mock, err := db.NewTestDb()
	assert.NoError(t, err, "An error was not expected")

	statusrows := sqlmock.NewRows([]string{"count"}).AddRow(0)
	mock.ExpectQuery(`SELECT count\(1\) FROM images`).WillReturnRows(statusrows)

	duperows := sqlmock.NewRows([]string{"count"}).AddRow(0)
	mock.ExpectQuery(`select count\(1\) from tagmap`).WillReturnRows(duperows)

	tag := AddTagModel{
		Ib:    1,
		Tag:   1,
		Image: 1,
	}

	err = tag.Status()
	if assert.Error(t, err, "An error was not expected") {
		assert.Equal(t, err, e.ErrNotFound, "Error should match")
	}

}

func TestAddTagStatusDuplicate(t *testing.T) {

	var err error

	mock, err := db.NewTestDb()
	assert.NoError(t, err, "An error was not expected")

	statusrows := sqlmock.NewRows([]string{"count"}).AddRow(1)
	mock.ExpectQuery(`SELECT count\(1\) FROM images`).WillReturnRows(statusrows)

	duperows := sqlmock.NewRows([]string{"count"}).AddRow(1)
	mock.ExpectQuery(`select count\(1\) from tagmap`).WillReturnRows(duperows)

	tag := AddTagModel{
		Ib:    1,
		Tag:   1,
		Image: 1,
	}

	err = tag.Status()
	if assert.Error(t, err, "An error was not expected") {
		assert.Equal(t, err, e.ErrDuplicateTag, "Error should match")
	}

}

func TestAddTagPost(t *testing.T) {

	var err error

	mock, err := db.NewTestDb()
	assert.NoError(t, err, "An error was not expected")

	mock.ExpectExec("INSERT into tagmap").
		WithArgs(1, 1).
		WillReturnResult(sqlmock.NewResult(1, 1))

	tag := AddTagModel{
		Ib:    1,
		Tag:   1,
		Image: 1,
	}

	err = tag.Post()
	assert.NoError(t, err, "An error was not expected")

}
<|endoftext|>


<commit_before>// +build integration

/*
Copyright 2018 The Kubernetes Authors All rights reserved.

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

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

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

package integration

import (
	"context"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
	"time"

	"k8s.io/apimachinery/pkg/util/wait"

	"github.com/pkg/errors"

	"k8s.io/minikube/pkg/kapi"
	"k8s.io/minikube/pkg/minikube/config"
	"k8s.io/minikube/pkg/util"
	"k8s.io/minikube/pkg/util/retry"
)

var tunnelSession StartSession

var (
	hostname = ""
	domain   = "nginx-svc.default.svc.cluster.local."
)

func validateTunnelCmd(ctx context.Context, t *testing.T, profile string) {
	ctx, cancel := context.WithTimeout(ctx, Minutes(20))
	type validateFunc func(context.Context, *testing.T, string)
	defer cancel()

	// Serial tests
	t.Run("serial", func(t *testing.T) {
		tests := []struct {
			name      string
			validator validateFunc
		}{
			{"StartTunnel", validateTunnelStart},                   // Start tunnel
			{"WaitService", validateServiceStable},                 // Wait for service is stable
			{"AccessDirect", validateAccessDirect},                 // Access test for loadbalancer IP
			{"DNSResolutionByDig", validateDNSDig},                 // DNS forwarding test by dig
			{"DNSResolutionByDscacheutil", validateDNSDscacheutil}, // DNS forwarding test by dscacheutil
			{"AccessThroughDNS", validateAccessDNS},                // Access test for absolute dns name
			{"DeleteTunnel", validateTunnelDelete},                 // Stop tunnel and delete cluster
		}
		for _, tc := range tests {
			tc := tc
			t.Run(tc.name, func(t *testing.T) {
				tc.validator(ctx, t, profile)
			})
		}
	})
}

// checkRoutePassword skips tunnel test if sudo password required for route
func checkRoutePassword(t *testing.T) {
	if !KicDriver() && runtime.GOOS != "windows" {
		if err := exec.Command("sudo", "-n", "ifconfig").Run(); err != nil {
			t.Skipf("password required to execute 'route', skipping testTunnel: %v", err)
		}
	}
}

// checkDNSForward skips DNS forwarding test if runtime is not supported
func checkDNSForward(t *testing.T) {
	// Not all platforms support DNS forwarding
	if runtime.GOOS != "darwin" {
		t.Skip("DNS forwarding is supported for darwin only now, skipping test DNS forwarding")
	}
}

// getKubeDNSIP returns kube-dns ClusterIP
func getKubeDNSIP(t *testing.T, profile string) string {
	// Load ClusterConfig
	c, err := config.Load(profile)
	if err != nil {
		t.Errorf("failed to load cluster config: %v", err)
	}
	// Get ipNet
	_, ipNet, err := net.ParseCIDR(c.KubernetesConfig.ServiceCIDR)
	if err != nil {
		t.Errorf("failed to parse service CIDR: %v", err)
	}
	// Get kube-dns ClusterIP
	ip, err := util.GetDNSIP(ipNet.String())
	if err != nil {
		t.Errorf("failed to get kube-dns IP: %v", err)
	}

	return ip.String()
}

// validateTunnelStart starts `minikube tunnel`
func validateTunnelStart(ctx context.Context, t *testing.T, profile string) {
	checkRoutePassword(t)

	args := []string{"-p", profile, "tunnel", "--alsologtostderr"}
	ss, err := Start(t, exec.CommandContext(ctx, Target(), args...))
	if err != nil {
		t.Errorf("failed to start a tunnel: args %q: %v", args, err)
	}
	tunnelSession = *ss
}

// validateServiceStable starts nginx pod, nginx service and waits nginx having loadbalancer ingress IP
func validateServiceStable(ctx context.Context, t *testing.T, profile string) {
	if HyperVDriver() {
		t.Skipf("skipping service test for hyperv driver ")
	}
	checkRoutePassword(t)

	client, err := kapi.Client(profile)
	if err != nil {
		t.Fatalf("failed to get Kubernetes client for %q: %v", profile, err)
	}

	// Start the "nginx" pod.
	rr, err := Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "apply", "-f", filepath.Join(*testdataDir, "testsvc.yaml")))
	if err != nil {
		t.Fatalf("%s failed: %v", rr.Command(), err)
	}
	if _, err := PodWait(ctx, t, profile, "default", "run=nginx-svc", Minutes(4)); err != nil {
		t.Fatalf("wait: %v", err)
	}

	if err := kapi.WaitForService(client, "default", "nginx-svc", true, 1*time.Second, Minutes(2)); err != nil {
		t.Fatal(errors.Wrap(err, "Error waiting for nginx service to be up"))
	}

	// Wait until the nginx-svc has a loadbalancer ingress IP
	err = wait.PollImmediate(5*time.Second, Minutes(3), func() (bool, error) {
		rr, err := Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "get", "svc", "nginx-svc", "-o", "jsonpath={.status.loadBalancer.ingress[0].ip}"))
		if err != nil {
			return false, err
		}
		if len(rr.Stdout.String()) > 0 {
			hostname = rr.Stdout.String()
			return true, nil
		}
		return false, nil
	})
	if err != nil {
		t.Errorf("nginx-svc svc.status.loadBalancer.ingress never got an IP")

		rr, err := Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "get", "svc", "nginx-svc"))
		if err != nil {
			t.Errorf("%s failed: %v", rr.Command(), err)
		}
		t.Logf("failed to kubectl get svc nginx-svc:\n%s", rr.Stdout)
	}
}

// validateAccessDirect validates if the test service can be accessed with LoadBalancer IP from host
func validateAccessDirect(ctx context.Context, t *testing.T, profile string) {
	if runtime.GOOS == "windows" {
		t.Skip("skipping: access direct test is broken on windows: https://github.com/kubernetes/minikube/issues/8304")
	}

	checkRoutePassword(t)

	got := []byte{}
	url := fmt.Sprintf("http://%s", hostname)

	fetch := func() error {
		h := &http.Client{Timeout: time.Second * 10}
		resp, err := h.Get(url)
		if err != nil {
			return &retry.RetriableError{Err: err}
		}
		if resp.Body == nil {
			return &retry.RetriableError{Err: fmt.Errorf("no body")}
		}
		defer resp.Body.Close()
		got, err = ioutil.ReadAll(resp.Body)
		if err != nil {
			return &retry.RetriableError{Err: err}
		}
		return nil
	}

	// Check if the nginx service can be accessed
	if err := retry.Expo(fetch, 3*time.Second, Minutes(2), 13); err != nil {
		t.Errorf("failed to hit nginx at %q: %v", url, err)

		rr, err := Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "get", "svc", "nginx-svc"))
		if err != nil {
			t.Errorf("%s failed: %v", rr.Command(), err)
		}
		t.Logf("failed to kubectl get svc nginx-svc:\n%s", rr.Stdout)
	}

	want := "Welcome to nginx!"
	if strings.Contains(string(got), want) {
		t.Logf("tunnel at %s is working!", url)
	} else {
		t.Errorf("expected body to contain %q, but got *%q*", want, got)
	}
}

// validateDNSDig validates if the DNS forwarding works by dig command DNS lookup
// NOTE: DNS forwarding is experimental: https://minikube.sigs.k8s.io/docs/handbook/accessing/#dns-resolution-experimental
func validateDNSDig(ctx context.Context, t *testing.T, profile string) {
	checkRoutePassword(t)
	checkDNSForward(t)

	ip := getKubeDNSIP(t, profile)
	dnsIP := fmt.Sprintf("@%s", ip)

	// Check if the dig DNS lookup works toward kube-dns IP
	rr, err := Run(t, exec.CommandContext(ctx, "dig", "+time=5", "+tries=3", dnsIP, domain, "A"))
	// dig command returns its output for stdout only. So we don't check stderr output.
	if err != nil {
		t.Errorf("failed to resolve DNS name: %v", err)
	}

	want := "ANSWER: 1"
	if strings.Contains(rr.Stdout.String(), want) {
		t.Logf("DNS resolution by dig for %s is working!", domain)
	} else {
		t.Errorf("expected body to contain %q, but got *%q*", want, rr.Stdout.String())

		// debug DNS configuration
		rr, err := Run(t, exec.CommandContext(ctx, "scutil", "--dns"))
		if err != nil {
			t.Errorf("%s failed: %v", rr.Command(), err)
		}
		t.Logf("debug for DNS configuration:\n%s", rr.Stdout.String())
	}
}

// validateDNSDscacheutil validates if the DNS forwarding works by dscacheutil command DNS lookup
// NOTE: DNS forwarding is experimental: https://minikube.sigs.k8s.io/docs/handbook/accessing/#dns-resolution-experimental
func validateDNSDscacheutil(ctx context.Context, t *testing.T, profile string) {
	checkRoutePassword(t)
	checkDNSForward(t)

	// Check if the dscacheutil DNS lookup works toward target domain
	rr, err := Run(t, exec.CommandContext(ctx, "dscacheutil", "-q", "host", "-a", "name", domain))
	// If dscacheutil cannot lookup dns record, it returns no output. So we don't check stderr output.
	if err != nil {
		t.Errorf("failed to resolve DNS name: %v", err)
	}

	want := hostname
	if strings.Contains(rr.Stdout.String(), want) {
		t.Logf("DNS resolution by dscacheutil for %s is working!", domain)
	} else {
		t.Errorf("expected body to contain %q, but got *%q*", want, rr.Stdout.String())
	}
}

// validateAccessDNS validates if the test service can be accessed with DNS forwarding from host
// NOTE: DNS forwarding is experimental: https://minikube.sigs.k8s.io/docs/handbook/accessing/#dns-resolution-experimental
func validateAccessDNS(ctx context.Context, t *testing.T, profile string) {
	checkRoutePassword(t)
	checkDNSForward(t)

	got := []byte{}
	url := fmt.Sprintf("http://%s", domain)

	ip := getKubeDNSIP(t, profile)
	dnsIP := fmt.Sprintf("%s:53", ip)

	// Set kube-dns dial
	kubeDNSDial := func(ctx context.Context, network, address string) (net.Conn, error) {
		d := net.Dialer{}
		return d.DialContext(ctx, "udp", dnsIP)
	}

	// Set kube-dns resolver
	r := net.Resolver{
		PreferGo: true,
		Dial:     kubeDNSDial,
	}
	dialer := net.Dialer{Resolver: &r}

	// Use kube-dns resolver
	transport := &http.Transport{
		Dial:        dialer.Dial,
		DialContext: dialer.DialContext,
	}

	fetch := func() error {
		h := &http.Client{Timeout: time.Second * 10, Transport: transport}
		resp, err := h.Get(url)
		if err != nil {
			return &retry.RetriableError{Err: err}
		}
		if resp.Body == nil {
			return &retry.RetriableError{Err: fmt.Errorf("no body")}
		}
		defer resp.Body.Close()
		got, err = ioutil.ReadAll(resp.Body)
		if err != nil {
			return &retry.RetriableError{Err: err}
		}
		return nil
	}

	// Access nginx-svc through DNS resolution
	if err := retry.Expo(fetch, 3*time.Second, Seconds(30), 10); err != nil {
		t.Errorf("failed to hit nginx with DNS forwarded %q: %v", url, err)
	}

	want := "Welcome to nginx!"
	if strings.Contains(string(got), want) {
		t.Logf("tunnel at %s is working!", url)
	} else {
		t.Errorf("expected body to contain %q, but got *%q*", want, got)
	}
}

// validateTunnelDelete stops `minikube tunnel`
func validateTunnelDelete(ctx context.Context, t *testing.T, profile string) {
	checkRoutePassword(t)
	// Stop tunnel
	tunnelSession.Stop(t)
}
<commit_msg>skip tunnell<commit_after>// +build integration

/*
Copyright 2018 The Kubernetes Authors All rights reserved.

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

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

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

package integration

import (
	"context"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
	"time"

	"k8s.io/apimachinery/pkg/util/wait"

	"github.com/pkg/errors"

	"k8s.io/minikube/pkg/kapi"
	"k8s.io/minikube/pkg/minikube/config"
	"k8s.io/minikube/pkg/util"
	"k8s.io/minikube/pkg/util/retry"
)

var tunnelSession StartSession

var (
	hostname = ""
	domain   = "nginx-svc.default.svc.cluster.local."
)

func validateTunnelCmd(ctx context.Context, t *testing.T, profile string) {
	ctx, cancel := context.WithTimeout(ctx, Minutes(20))
	type validateFunc func(context.Context, *testing.T, string)
	defer cancel()

	// Serial tests
	t.Run("serial", func(t *testing.T) {
		tests := []struct {
			name      string
			validator validateFunc
		}{
			{"StartTunnel", validateTunnelStart},                   // Start tunnel
			{"WaitService", validateServiceStable},                 // Wait for service is stable
			{"AccessDirect", validateAccessDirect},                 // Access test for loadbalancer IP
			{"DNSResolutionByDig", validateDNSDig},                 // DNS forwarding test by dig
			{"DNSResolutionByDscacheutil", validateDNSDscacheutil}, // DNS forwarding test by dscacheutil
			{"AccessThroughDNS", validateAccessDNS},                // Access test for absolute dns name
			{"DeleteTunnel", validateTunnelDelete},                 // Stop tunnel and delete cluster
		}
		for _, tc := range tests {
			tc := tc
			t.Run(tc.name, func(t *testing.T) {
				tc.validator(ctx, t, profile)
			})
		}
	})
}

// checkRoutePassword skips tunnel test if sudo password required for route
func checkRoutePassword(t *testing.T) {
	if !KicDriver() && runtime.GOOS != "windows" {
		if err := exec.Command("sudo", "-n", "ifconfig").Run(); err != nil {
			t.Skipf("password required to execute 'route', skipping testTunnel: %v", err)
		}
	}
}

// checkDNSForward skips DNS forwarding test if runtime is not supported
func checkDNSForward(t *testing.T) {
	// Not all platforms support DNS forwarding
	if runtime.GOOS != "darwin" {
		t.Skip("DNS forwarding is supported for darwin only now, skipping test DNS forwarding")
	}
}

// getKubeDNSIP returns kube-dns ClusterIP
func getKubeDNSIP(t *testing.T, profile string) string {
	// Load ClusterConfig
	c, err := config.Load(profile)
	if err != nil {
		t.Errorf("failed to load cluster config: %v", err)
	}
	// Get ipNet
	_, ipNet, err := net.ParseCIDR(c.KubernetesConfig.ServiceCIDR)
	if err != nil {
		t.Errorf("failed to parse service CIDR: %v", err)
	}
	// Get kube-dns ClusterIP
	ip, err := util.GetDNSIP(ipNet.String())
	if err != nil {
		t.Errorf("failed to get kube-dns IP: %v", err)
	}

	return ip.String()
}

// validateTunnelStart starts `minikube tunnel`
func validateTunnelStart(ctx context.Context, t *testing.T, profile string) {
	if HyperVDriver() {
		t.Skipf("skipping tunnel for hyperv driver")
	}
	checkRoutePassword(t)

	args := []string{"-p", profile, "tunnel", "--alsologtostderr"}
	ss, err := Start(t, exec.CommandContext(ctx, Target(), args...))
	if err != nil {
		t.Errorf("failed to start a tunnel: args %q: %v", args, err)
	}
	tunnelSession = *ss
}

// validateServiceStable starts nginx pod, nginx service and waits nginx having loadbalancer ingress IP
func validateServiceStable(ctx context.Context, t *testing.T, profile string) {
	if HyperVDriver() {
		t.Skipf("skipping service test for hyperv driver ")
	}
	checkRoutePassword(t)

	client, err := kapi.Client(profile)
	if err != nil {
		t.Fatalf("failed to get Kubernetes client for %q: %v", profile, err)
	}

	// Start the "nginx" pod.
	rr, err := Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "apply", "-f", filepath.Join(*testdataDir, "testsvc.yaml")))
	if err != nil {
		t.Fatalf("%s failed: %v", rr.Command(), err)
	}
	if _, err := PodWait(ctx, t, profile, "default", "run=nginx-svc", Minutes(4)); err != nil {
		t.Fatalf("wait: %v", err)
	}

	if err := kapi.WaitForService(client, "default", "nginx-svc", true, 1*time.Second, Minutes(2)); err != nil {
		t.Fatal(errors.Wrap(err, "Error waiting for nginx service to be up"))
	}

	// Wait until the nginx-svc has a loadbalancer ingress IP
	err = wait.PollImmediate(5*time.Second, Minutes(3), func() (bool, error) {
		rr, err := Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "get", "svc", "nginx-svc", "-o", "jsonpath={.status.loadBalancer.ingress[0].ip}"))
		if err != nil {
			return false, err
		}
		if len(rr.Stdout.String()) > 0 {
			hostname = rr.Stdout.String()
			return true, nil
		}
		return false, nil
	})
	if err != nil {
		t.Errorf("nginx-svc svc.status.loadBalancer.ingress never got an IP")

		rr, err := Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "get", "svc", "nginx-svc"))
		if err != nil {
			t.Errorf("%s failed: %v", rr.Command(), err)
		}
		t.Logf("failed to kubectl get svc nginx-svc:\n%s", rr.Stdout)
	}
}

// validateAccessDirect validates if the test service can be accessed with LoadBalancer IP from host
func validateAccessDirect(ctx context.Context, t *testing.T, profile string) {
	if runtime.GOOS == "windows" {
		t.Skip("skipping: access direct test is broken on windows: https://github.com/kubernetes/minikube/issues/8304")
	}

	checkRoutePassword(t)

	got := []byte{}
	url := fmt.Sprintf("http://%s", hostname)

	fetch := func() error {
		h := &http.Client{Timeout: time.Second * 10}
		resp, err := h.Get(url)
		if err != nil {
			return &retry.RetriableError{Err: err}
		}
		if resp.Body == nil {
			return &retry.RetriableError{Err: fmt.Errorf("no body")}
		}
		defer resp.Body.Close()
		got, err = ioutil.ReadAll(resp.Body)
		if err != nil {
			return &retry.RetriableError{Err: err}
		}
		return nil
	}

	// Check if the nginx service can be accessed
	if err := retry.Expo(fetch, 3*time.Second, Minutes(2), 13); err != nil {
		t.Errorf("failed to hit nginx at %q: %v", url, err)

		rr, err := Run(t, exec.CommandContext(ctx, "kubectl", "--context", profile, "get", "svc", "nginx-svc"))
		if err != nil {
			t.Errorf("%s failed: %v", rr.Command(), err)
		}
		t.Logf("failed to kubectl get svc nginx-svc:\n%s", rr.Stdout)
	}

	want := "Welcome to nginx!"
	if strings.Contains(string(got), want) {
		t.Logf("tunnel at %s is working!", url)
	} else {
		t.Errorf("expected body to contain %q, but got *%q*", want, got)
	}
}

// validateDNSDig validates if the DNS forwarding works by dig command DNS lookup
// NOTE: DNS forwarding is experimental: https://minikube.sigs.k8s.io/docs/handbook/accessing/#dns-resolution-experimental
func validateDNSDig(ctx context.Context, t *testing.T, profile string) {
	checkRoutePassword(t)
	checkDNSForward(t)

	ip := getKubeDNSIP(t, profile)
	dnsIP := fmt.Sprintf("@%s", ip)

	// Check if the dig DNS lookup works toward kube-dns IP
	rr, err := Run(t, exec.CommandContext(ctx, "dig", "+time=5", "+tries=3", dnsIP, domain, "A"))
	// dig command returns its output for stdout only. So we don't check stderr output.
	if err != nil {
		t.Errorf("failed to resolve DNS name: %v", err)
	}

	want := "ANSWER: 1"
	if strings.Contains(rr.Stdout.String(), want) {
		t.Logf("DNS resolution by dig for %s is working!", domain)
	} else {
		t.Errorf("expected body to contain %q, but got *%q*", want, rr.Stdout.String())

		// debug DNS configuration
		rr, err := Run(t, exec.CommandContext(ctx, "scutil", "--dns"))
		if err != nil {
			t.Errorf("%s failed: %v", rr.Command(), err)
		}
		t.Logf("debug for DNS configuration:\n%s", rr.Stdout.String())
	}
}

// validateDNSDscacheutil validates if the DNS forwarding works by dscacheutil command DNS lookup
// NOTE: DNS forwarding is experimental: https://minikube.sigs.k8s.io/docs/handbook/accessing/#dns-resolution-experimental
func validateDNSDscacheutil(ctx context.Context, t *testing.T, profile string) {
	checkRoutePassword(t)
	checkDNSForward(t)

	// Check if the dscacheutil DNS lookup works toward target domain
	rr, err := Run(t, exec.CommandContext(ctx, "dscacheutil", "-q", "host", "-a", "name", domain))
	// If dscacheutil cannot lookup dns record, it returns no output. So we don't check stderr output.
	if err != nil {
		t.Errorf("failed to resolve DNS name: %v", err)
	}

	want := hostname
	if strings.Contains(rr.Stdout.String(), want) {
		t.Logf("DNS resolution by dscacheutil for %s is working!", domain)
	} else {
		t.Errorf("expected body to contain %q, but got *%q*", want, rr.Stdout.String())
	}
}

// validateAccessDNS validates if the test service can be accessed with DNS forwarding from host
// NOTE: DNS forwarding is experimental: https://minikube.sigs.k8s.io/docs/handbook/accessing/#dns-resolution-experimental
func validateAccessDNS(ctx context.Context, t *testing.T, profile string) {
	checkRoutePassword(t)
	checkDNSForward(t)

	got := []byte{}
	url := fmt.Sprintf("http://%s", domain)

	ip := getKubeDNSIP(t, profile)
	dnsIP := fmt.Sprintf("%s:53", ip)

	// Set kube-dns dial
	kubeDNSDial := func(ctx context.Context, network, address string) (net.Conn, error) {
		d := net.Dialer{}
		return d.DialContext(ctx, "udp", dnsIP)
	}

	// Set kube-dns resolver
	r := net.Resolver{
		PreferGo: true,
		Dial:     kubeDNSDial,
	}
	dialer := net.Dialer{Resolver: &r}

	// Use kube-dns resolver
	transport := &http.Transport{
		Dial:        dialer.Dial,
		DialContext: dialer.DialContext,
	}

	fetch := func() error {
		h := &http.Client{Timeout: time.Second * 10, Transport: transport}
		resp, err := h.Get(url)
		if err != nil {
			return &retry.RetriableError{Err: err}
		}
		if resp.Body == nil {
			return &retry.RetriableError{Err: fmt.Errorf("no body")}
		}
		defer resp.Body.Close()
		got, err = ioutil.ReadAll(resp.Body)
		if err != nil {
			return &retry.RetriableError{Err: err}
		}
		return nil
	}

	// Access nginx-svc through DNS resolution
	if err := retry.Expo(fetch, 3*time.Second, Seconds(30), 10); err != nil {
		t.Errorf("failed to hit nginx with DNS forwarded %q: %v", url, err)
	}

	want := "Welcome to nginx!"
	if strings.Contains(string(got), want) {
		t.Logf("tunnel at %s is working!", url)
	} else {
		t.Errorf("expected body to contain %q, but got *%q*", want, got)
	}
}

// validateTunnelDelete stops `minikube tunnel`
func validateTunnelDelete(ctx context.Context, t *testing.T, profile string) {
	if HyperVDriver() {
		t.Skipf("skipping tunnel for hyperv driver")
	}
	checkRoutePassword(t)
	// Stop tunnel
	tunnelSession.Stop(t)
}
<|endoftext|>


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

// Copyright 2015 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package evaluator

import (
	"strings"

	"github.com/pingcap/tidb/ast"
	"github.com/pingcap/tidb/context"
	"github.com/pingcap/tidb/parser/opcode"
	"github.com/pingcap/tidb/util/types"
)

// BuiltinFunc is the function signature for builtin functions
type BuiltinFunc func([]types.Datum, context.Context) (types.Datum, error)

// Func is for a builtin function.
type Func struct {
	// F is the specific calling function.
	F BuiltinFunc
	// MinArgs is the minimal arguments needed,
	MinArgs int
	// MaxArgs is the maximal arguments needed, -1 for infinity.
	MaxArgs int
}

// Funcs holds all registered builtin functions.
var Funcs = map[string]Func{
	// common functions
	ast.Coalesce: {builtinCoalesce, 1, -1},
	ast.IsNull:   {builtinIsNull, 1, 1},
	ast.Greatest: {builtinGreatest, 2, -1},

	// math functions
	ast.Abs:     {builtinAbs, 1, 1},
	ast.Ceil:    {builtinCeil, 1, 1},
	ast.Ceiling: {builtinCeil, 1, 1},
	ast.Ln:      {builtinLog, 1, 1},
	ast.Log:     {builtinLog, 1, 2},
	ast.Log2:    {builtinLog2, 1, 1},
	ast.Log10:   {builtinLog10, 1, 1},
	ast.Pow:     {builtinPow, 2, 2},
	ast.Power:   {builtinPow, 2, 2},
	ast.Rand:    {builtinRand, 0, 1},
	ast.Round:   {builtinRound, 1, 2},

	// time functions
	ast.Curdate:          {builtinCurrentDate, 0, 0},
	ast.CurrentDate:      {builtinCurrentDate, 0, 0},
	ast.CurrentTime:      {builtinCurrentTime, 0, 1},
	ast.Date:             {builtinDate, 1, 1},
	ast.DateArith:        {builtinDateArith, 3, 3},
	ast.DateFormat:       {builtinDateFormat, 2, 2},
	ast.CurrentTimestamp: {builtinNow, 0, 1},
	ast.Curtime:          {builtinCurrentTime, 0, 1},
	ast.Day:              {builtinDay, 1, 1},
	ast.DayName:          {builtinDayName, 1, 1},
	ast.DayOfMonth:       {builtinDayOfMonth, 1, 1},
	ast.DayOfWeek:        {builtinDayOfWeek, 1, 1},
	ast.DayOfYear:        {builtinDayOfYear, 1, 1},
	ast.Extract:          {builtinExtract, 2, 2},
	ast.Hour:             {builtinHour, 1, 1},
	ast.MicroSecond:      {builtinMicroSecond, 1, 1},
	ast.Minute:           {builtinMinute, 1, 1},
	ast.Month:            {builtinMonth, 1, 1},
	ast.MonthName:        {builtinMonthName, 1, 1},
	ast.Now:              {builtinNow, 0, 1},
	ast.Second:           {builtinSecond, 1, 1},
	ast.StrToDate:        {builtinStrToDate, 2, 2},
	ast.Sysdate:          {builtinSysDate, 0, 1},
	ast.Time:             {builtinTime, 1, 1},
	ast.UTCDate:          {builtinUTCDate, 0, 0},
	ast.Week:             {builtinWeek, 1, 2},
	ast.Weekday:          {builtinWeekDay, 1, 1},
	ast.WeekOfYear:       {builtinWeekOfYear, 1, 1},
	ast.Year:             {builtinYear, 1, 1},
	ast.YearWeek:         {builtinYearWeek, 1, 2},
	ast.FromUnixTime:     {builtinFromUnixTime, 1, 2},
	ast.TimeDiff:         {builtinTimeDiff, 2, 2},

	// string functions
	ast.ASCII:          {builtinASCII, 1, 1},
	ast.Concat:         {builtinConcat, 1, -1},
	ast.ConcatWS:       {builtinConcatWS, 2, -1},
	ast.Convert:        {builtinConvert, 2, 2},
	ast.Lcase:          {builtinLower, 1, 1},
	ast.Left:           {builtinLeft, 2, 2},
	ast.Length:         {builtinLength, 1, 1},
	ast.Locate:         {builtinLocate, 2, 3},
	ast.Lower:          {builtinLower, 1, 1},
	ast.Ltrim:          {trimFn(strings.TrimLeft, spaceChars), 1, 1},
	ast.Repeat:         {builtinRepeat, 2, 2},
	ast.Replace:        {builtinReplace, 3, 3},
	ast.Reverse:        {builtinReverse, 1, 1},
	ast.Rtrim:          {trimFn(strings.TrimRight, spaceChars), 1, 1},
	ast.Space:          {builtinSpace, 1, 1},
	ast.Strcmp:         {builtinStrcmp, 2, 2},
	ast.Substring:      {builtinSubstring, 2, 3},
	ast.SubstringIndex: {builtinSubstringIndex, 3, 3},
	ast.Trim:           {builtinTrim, 1, 3},
	ast.Upper:          {builtinUpper, 1, 1},
	ast.Ucase:          {builtinUpper, 1, 1},
	ast.Hex:            {builtinHex, 1, 1},
	ast.Unhex:          {builtinUnHex, 1, 1},
	ast.Rpad:           {builtinRpad, 3, 3},

	// information functions
	ast.ConnectionID: {builtinConnectionID, 0, 0},
	ast.CurrentUser:  {builtinCurrentUser, 0, 0},
	ast.Database:     {builtinDatabase, 0, 0},
	// This function is a synonym for DATABASE().
	// See http://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_schema
	ast.Schema:       {builtinDatabase, 0, 0},
	ast.FoundRows:    {builtinFoundRows, 0, 0},
	ast.LastInsertId: {builtinLastInsertID, 0, 1},
	ast.User:         {builtinUser, 0, 0},
	ast.Version:      {builtinVersion, 0, 0},

	// control functions
	ast.If:     {builtinIf, 3, 3},
	ast.Ifnull: {builtinIfNull, 2, 2},
	ast.Nullif: {builtinNullIf, 2, 2},

	// miscellaneous functions
	ast.Sleep: {builtinSleep, 1, 1},

	// get_lock() and release_lock() is parsed but do nothing.
	// It is used for preventing error in Ruby's activerecord migrations.
	ast.GetLock:     {builtinLock, 2, 2},
	ast.ReleaseLock: {builtinReleaseLock, 1, 1},

	// only used by new plan
	ast.AndAnd:     {builtinAndAnd, 2, 2},
	ast.OrOr:       {builtinOrOr, 2, 2},
	ast.GE:         {compareFuncFactory(opcode.GE), 2, 2},
	ast.LE:         {compareFuncFactory(opcode.LE), 2, 2},
	ast.EQ:         {compareFuncFactory(opcode.EQ), 2, 2},
	ast.NE:         {compareFuncFactory(opcode.NE), 2, 2},
	ast.LT:         {compareFuncFactory(opcode.LT), 2, 2},
	ast.GT:         {compareFuncFactory(opcode.GT), 2, 2},
	ast.NullEQ:     {compareFuncFactory(opcode.NullEQ), 2, 2},
	ast.Plus:       {arithmeticFuncFactory(opcode.Plus), 2, 2},
	ast.Minus:      {arithmeticFuncFactory(opcode.Minus), 2, 2},
	ast.Mod:        {arithmeticFuncFactory(opcode.Mod), 2, 2},
	ast.Div:        {arithmeticFuncFactory(opcode.Div), 2, 2},
	ast.Mul:        {arithmeticFuncFactory(opcode.Mul), 2, 2},
	ast.IntDiv:     {arithmeticFuncFactory(opcode.IntDiv), 2, 2},
	ast.LeftShift:  {bitOpFactory(opcode.LeftShift), 2, 2},
	ast.RightShift: {bitOpFactory(opcode.RightShift), 2, 2},
	ast.And:        {bitOpFactory(opcode.And), 2, 2},
	ast.Or:         {bitOpFactory(opcode.Or), 2, 2},
	ast.Xor:        {bitOpFactory(opcode.Xor), 2, 2},
	ast.LogicXor:   {builtinLogicXor, 2, 2},
	ast.UnaryNot:   {unaryOpFactory(opcode.Not), 1, 1},
	ast.BitNeg:     {unaryOpFactory(opcode.BitNeg), 1, 1},
	ast.UnaryPlus:  {unaryOpFactory(opcode.Plus), 1, 1},
	ast.UnaryMinus: {unaryOpFactory(opcode.Minus), 1, 1},
	ast.In:         {builtinIn, 1, -1},
	ast.IsTruth:    {isTrueOpFactory(opcode.IsTruth), 1, 1},
	ast.IsFalsity:  {isTrueOpFactory(opcode.IsFalsity), 1, 1},
	ast.Like:       {builtinLike, 3, 3},
	ast.Regexp:     {builtinRegexp, 2, 2},
	ast.Case:       {builtinCaseWhen, 1, -1},
	ast.RowFunc:    {builtinRow, 2, -1},
	ast.SetVar:     {builtinSetVar, 2, 2},
	ast.GetVar:     {builtinGetVar, 1, 1},
}

// DynamicFuncs are those functions that
// use input parameter ctx or
// return an uncertain result would not be constant folded
// the value 0 means nothing
var DynamicFuncs = map[string]int{
	"rand":           0,
	"connection_id":  0,
	"current_user":   0,
	"database":       0,
	"found_rows":     0,
	"last_insert_id": 0,
	"user":           0,
	"version":        0,
	"sleep":          0,
	ast.GetVar:       0,
	ast.SetVar:       0,
}

// See http://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#function_coalesce
func builtinCoalesce(args []types.Datum, ctx context.Context) (d types.Datum, err error) {
	for _, d = range args {
		if !d.IsNull() {
			return d, nil
		}
	}
	return d, nil
}

// See https://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#function_isnull
func builtinIsNull(args []types.Datum, _ context.Context) (d types.Datum, err error) {
	if args[0].IsNull() {
		d.SetInt64(1)
	} else {
		d.SetInt64(0)
	}
	return d, nil
}

// See http://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#function_greatest
func builtinGreatest(args []types.Datum, ctx context.Context) (d types.Datum, err error) {
	max := 0
	sc := ctx.GetSessionVars().StmtCtx
	for i := 0; i < len(args); i++ {
		if args[i].IsNull() {
			d.SetNull()
			return
		}

		var cmp int
		if cmp, err = args[i].CompareDatum(sc, args[max]); err != nil {
			return
		}

		if cmp > 0 {
			max = i
		}
	}
	d = args[max]
	return
}
<commit_msg>evaluator: makes `values` function dynamic. (#2320)<commit_after>// Copyright 2013 The ql Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSES/QL-LICENSE file.

// Copyright 2015 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package evaluator

import (
	"strings"

	"github.com/pingcap/tidb/ast"
	"github.com/pingcap/tidb/context"
	"github.com/pingcap/tidb/parser/opcode"
	"github.com/pingcap/tidb/util/types"
)

// BuiltinFunc is the function signature for builtin functions
type BuiltinFunc func([]types.Datum, context.Context) (types.Datum, error)

// Func is for a builtin function.
type Func struct {
	// F is the specific calling function.
	F BuiltinFunc
	// MinArgs is the minimal arguments needed,
	MinArgs int
	// MaxArgs is the maximal arguments needed, -1 for infinity.
	MaxArgs int
}

// Funcs holds all registered builtin functions.
var Funcs = map[string]Func{
	// common functions
	ast.Coalesce: {builtinCoalesce, 1, -1},
	ast.IsNull:   {builtinIsNull, 1, 1},
	ast.Greatest: {builtinGreatest, 2, -1},

	// math functions
	ast.Abs:     {builtinAbs, 1, 1},
	ast.Ceil:    {builtinCeil, 1, 1},
	ast.Ceiling: {builtinCeil, 1, 1},
	ast.Ln:      {builtinLog, 1, 1},
	ast.Log:     {builtinLog, 1, 2},
	ast.Log2:    {builtinLog2, 1, 1},
	ast.Log10:   {builtinLog10, 1, 1},
	ast.Pow:     {builtinPow, 2, 2},
	ast.Power:   {builtinPow, 2, 2},
	ast.Rand:    {builtinRand, 0, 1},
	ast.Round:   {builtinRound, 1, 2},

	// time functions
	ast.Curdate:          {builtinCurrentDate, 0, 0},
	ast.CurrentDate:      {builtinCurrentDate, 0, 0},
	ast.CurrentTime:      {builtinCurrentTime, 0, 1},
	ast.Date:             {builtinDate, 1, 1},
	ast.DateArith:        {builtinDateArith, 3, 3},
	ast.DateFormat:       {builtinDateFormat, 2, 2},
	ast.CurrentTimestamp: {builtinNow, 0, 1},
	ast.Curtime:          {builtinCurrentTime, 0, 1},
	ast.Day:              {builtinDay, 1, 1},
	ast.DayName:          {builtinDayName, 1, 1},
	ast.DayOfMonth:       {builtinDayOfMonth, 1, 1},
	ast.DayOfWeek:        {builtinDayOfWeek, 1, 1},
	ast.DayOfYear:        {builtinDayOfYear, 1, 1},
	ast.Extract:          {builtinExtract, 2, 2},
	ast.Hour:             {builtinHour, 1, 1},
	ast.MicroSecond:      {builtinMicroSecond, 1, 1},
	ast.Minute:           {builtinMinute, 1, 1},
	ast.Month:            {builtinMonth, 1, 1},
	ast.MonthName:        {builtinMonthName, 1, 1},
	ast.Now:              {builtinNow, 0, 1},
	ast.Second:           {builtinSecond, 1, 1},
	ast.StrToDate:        {builtinStrToDate, 2, 2},
	ast.Sysdate:          {builtinSysDate, 0, 1},
	ast.Time:             {builtinTime, 1, 1},
	ast.UTCDate:          {builtinUTCDate, 0, 0},
	ast.Week:             {builtinWeek, 1, 2},
	ast.Weekday:          {builtinWeekDay, 1, 1},
	ast.WeekOfYear:       {builtinWeekOfYear, 1, 1},
	ast.Year:             {builtinYear, 1, 1},
	ast.YearWeek:         {builtinYearWeek, 1, 2},
	ast.FromUnixTime:     {builtinFromUnixTime, 1, 2},
	ast.TimeDiff:         {builtinTimeDiff, 2, 2},

	// string functions
	ast.ASCII:          {builtinASCII, 1, 1},
	ast.Concat:         {builtinConcat, 1, -1},
	ast.ConcatWS:       {builtinConcatWS, 2, -1},
	ast.Convert:        {builtinConvert, 2, 2},
	ast.Lcase:          {builtinLower, 1, 1},
	ast.Left:           {builtinLeft, 2, 2},
	ast.Length:         {builtinLength, 1, 1},
	ast.Locate:         {builtinLocate, 2, 3},
	ast.Lower:          {builtinLower, 1, 1},
	ast.Ltrim:          {trimFn(strings.TrimLeft, spaceChars), 1, 1},
	ast.Repeat:         {builtinRepeat, 2, 2},
	ast.Replace:        {builtinReplace, 3, 3},
	ast.Reverse:        {builtinReverse, 1, 1},
	ast.Rtrim:          {trimFn(strings.TrimRight, spaceChars), 1, 1},
	ast.Space:          {builtinSpace, 1, 1},
	ast.Strcmp:         {builtinStrcmp, 2, 2},
	ast.Substring:      {builtinSubstring, 2, 3},
	ast.SubstringIndex: {builtinSubstringIndex, 3, 3},
	ast.Trim:           {builtinTrim, 1, 3},
	ast.Upper:          {builtinUpper, 1, 1},
	ast.Ucase:          {builtinUpper, 1, 1},
	ast.Hex:            {builtinHex, 1, 1},
	ast.Unhex:          {builtinUnHex, 1, 1},
	ast.Rpad:           {builtinRpad, 3, 3},

	// information functions
	ast.ConnectionID: {builtinConnectionID, 0, 0},
	ast.CurrentUser:  {builtinCurrentUser, 0, 0},
	ast.Database:     {builtinDatabase, 0, 0},
	// This function is a synonym for DATABASE().
	// See http://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_schema
	ast.Schema:       {builtinDatabase, 0, 0},
	ast.FoundRows:    {builtinFoundRows, 0, 0},
	ast.LastInsertId: {builtinLastInsertID, 0, 1},
	ast.User:         {builtinUser, 0, 0},
	ast.Version:      {builtinVersion, 0, 0},

	// control functions
	ast.If:     {builtinIf, 3, 3},
	ast.Ifnull: {builtinIfNull, 2, 2},
	ast.Nullif: {builtinNullIf, 2, 2},

	// miscellaneous functions
	ast.Sleep: {builtinSleep, 1, 1},

	// get_lock() and release_lock() is parsed but do nothing.
	// It is used for preventing error in Ruby's activerecord migrations.
	ast.GetLock:     {builtinLock, 2, 2},
	ast.ReleaseLock: {builtinReleaseLock, 1, 1},

	// only used by new plan
	ast.AndAnd:     {builtinAndAnd, 2, 2},
	ast.OrOr:       {builtinOrOr, 2, 2},
	ast.GE:         {compareFuncFactory(opcode.GE), 2, 2},
	ast.LE:         {compareFuncFactory(opcode.LE), 2, 2},
	ast.EQ:         {compareFuncFactory(opcode.EQ), 2, 2},
	ast.NE:         {compareFuncFactory(opcode.NE), 2, 2},
	ast.LT:         {compareFuncFactory(opcode.LT), 2, 2},
	ast.GT:         {compareFuncFactory(opcode.GT), 2, 2},
	ast.NullEQ:     {compareFuncFactory(opcode.NullEQ), 2, 2},
	ast.Plus:       {arithmeticFuncFactory(opcode.Plus), 2, 2},
	ast.Minus:      {arithmeticFuncFactory(opcode.Minus), 2, 2},
	ast.Mod:        {arithmeticFuncFactory(opcode.Mod), 2, 2},
	ast.Div:        {arithmeticFuncFactory(opcode.Div), 2, 2},
	ast.Mul:        {arithmeticFuncFactory(opcode.Mul), 2, 2},
	ast.IntDiv:     {arithmeticFuncFactory(opcode.IntDiv), 2, 2},
	ast.LeftShift:  {bitOpFactory(opcode.LeftShift), 2, 2},
	ast.RightShift: {bitOpFactory(opcode.RightShift), 2, 2},
	ast.And:        {bitOpFactory(opcode.And), 2, 2},
	ast.Or:         {bitOpFactory(opcode.Or), 2, 2},
	ast.Xor:        {bitOpFactory(opcode.Xor), 2, 2},
	ast.LogicXor:   {builtinLogicXor, 2, 2},
	ast.UnaryNot:   {unaryOpFactory(opcode.Not), 1, 1},
	ast.BitNeg:     {unaryOpFactory(opcode.BitNeg), 1, 1},
	ast.UnaryPlus:  {unaryOpFactory(opcode.Plus), 1, 1},
	ast.UnaryMinus: {unaryOpFactory(opcode.Minus), 1, 1},
	ast.In:         {builtinIn, 1, -1},
	ast.IsTruth:    {isTrueOpFactory(opcode.IsTruth), 1, 1},
	ast.IsFalsity:  {isTrueOpFactory(opcode.IsFalsity), 1, 1},
	ast.Like:       {builtinLike, 3, 3},
	ast.Regexp:     {builtinRegexp, 2, 2},
	ast.Case:       {builtinCaseWhen, 1, -1},
	ast.RowFunc:    {builtinRow, 2, -1},
	ast.SetVar:     {builtinSetVar, 2, 2},
	ast.GetVar:     {builtinGetVar, 1, 1},
}

// DynamicFuncs are those functions that
// use input parameter ctx or
// return an uncertain result would not be constant folded
// the value 0 means nothing
var DynamicFuncs = map[string]int{
	"rand":           0,
	"connection_id":  0,
	"current_user":   0,
	"database":       0,
	"found_rows":     0,
	"last_insert_id": 0,
	"user":           0,
	"version":        0,
	"sleep":          0,
	ast.GetVar:       0,
	ast.SetVar:       0,
	ast.Values:       0,
}

// See http://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#function_coalesce
func builtinCoalesce(args []types.Datum, ctx context.Context) (d types.Datum, err error) {
	for _, d = range args {
		if !d.IsNull() {
			return d, nil
		}
	}
	return d, nil
}

// See https://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#function_isnull
func builtinIsNull(args []types.Datum, _ context.Context) (d types.Datum, err error) {
	if args[0].IsNull() {
		d.SetInt64(1)
	} else {
		d.SetInt64(0)
	}
	return d, nil
}

// See http://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#function_greatest
func builtinGreatest(args []types.Datum, ctx context.Context) (d types.Datum, err error) {
	max := 0
	sc := ctx.GetSessionVars().StmtCtx
	for i := 0; i < len(args); i++ {
		if args[i].IsNull() {
			d.SetNull()
			return
		}

		var cmp int
		if cmp, err = args[i].CompareDatum(sc, args[max]); err != nil {
			return
		}

		if cmp > 0 {
			max = i
		}
	}
	d = args[max]
	return
}
<|endoftext|>


<commit_before>/*
 * Copyright 2014 VMware, Inc.  All rights reserved.  Licensed under the Apache v2 License.
 */

package vmwarefusion

const vmx = `
.encoding = "UTF-8"
config.version = "8"
displayName = "{{.MachineName}}"
ethernet0.present = "TRUE"
ethernet0.connectionType = "nat"
ethernet0.virtualDev = "vmxnet3"
ethernet0.wakeOnPcktRcv = "FALSE"
ethernet0.addressType = "generated"
ethernet0.linkStatePropagation.enable = "TRUE"
pciBridge0.present = "TRUE"
pciBridge4.present = "TRUE"
pciBridge4.virtualDev = "pcieRootPort"
pciBridge4.functions = "8"
pciBridge5.present = "TRUE"
pciBridge5.virtualDev = "pcieRootPort"
pciBridge5.functions = "8"
pciBridge6.present = "TRUE"
pciBridge6.virtualDev = "pcieRootPort"
pciBridge6.functions = "8"
pciBridge7.present = "TRUE"
pciBridge7.virtualDev = "pcieRootPort"
pciBridge7.functions = "8"
pciBridge0.pciSlotNumber = "17"
pciBridge4.pciSlotNumber = "21"
pciBridge5.pciSlotNumber = "22"
pciBridge6.pciSlotNumber = "23"
pciBridge7.pciSlotNumber = "24"
scsi0.pciSlotNumber = "160"
usb.pciSlotNumber = "32"
ethernet0.pciSlotNumber = "192"
sound.pciSlotNumber = "33"
vmci0.pciSlotNumber = "35"
sata0.pciSlotNumber = "36"
floppy0.present = "FALSE"
guestOS = "other3xlinux-64"
hpet0.present = "TRUE"
sata0.present = "TRUE"
sata0:1.present = "TRUE"
sata0:1.fileName = "{{.ISO}}"
sata0:1.deviceType = "cdrom-image"
{{ if .ConfigDriveURL }}
sata0:2.present = "TRUE"
sata0:2.fileName = "{{.ConfigDriveISO}}"
sata0:2.deviceType = "cdrom-image"
{{ end }}
vmci0.present = "TRUE"
mem.hotadd = "TRUE"
memsize = "{{.Memory}}"
powerType.powerOff = "soft"
powerType.powerOn = "soft"
powerType.reset = "soft"
powerType.suspend = "soft"
scsi0.present = "TRUE"
scsi0.virtualDev = "pvscsi"
scsi0:0.fileName = "{{.MachineName}}.vmdk"
scsi0:0.present = "TRUE"
virtualHW.productCompatibility = "hosted"
virtualHW.version = "10"
msg.autoanswer = "TRUE"
uuid.action = "create"
numvcpus = "{{.CPU}}"
hgfs.mapRootShare = "FALSE"
hgfs.linkRootShare = "FALSE"
`
<commit_msg>Enable syncing time for VMWare Fusion guests<commit_after>/*
 * Copyright 2014 VMware, Inc.  All rights reserved.  Licensed under the Apache v2 License.
 */

package vmwarefusion

const vmx = `
.encoding = "UTF-8"
config.version = "8"
displayName = "{{.MachineName}}"
ethernet0.present = "TRUE"
ethernet0.connectionType = "nat"
ethernet0.virtualDev = "vmxnet3"
ethernet0.wakeOnPcktRcv = "FALSE"
ethernet0.addressType = "generated"
ethernet0.linkStatePropagation.enable = "TRUE"
pciBridge0.present = "TRUE"
pciBridge4.present = "TRUE"
pciBridge4.virtualDev = "pcieRootPort"
pciBridge4.functions = "8"
pciBridge5.present = "TRUE"
pciBridge5.virtualDev = "pcieRootPort"
pciBridge5.functions = "8"
pciBridge6.present = "TRUE"
pciBridge6.virtualDev = "pcieRootPort"
pciBridge6.functions = "8"
pciBridge7.present = "TRUE"
pciBridge7.virtualDev = "pcieRootPort"
pciBridge7.functions = "8"
pciBridge0.pciSlotNumber = "17"
pciBridge4.pciSlotNumber = "21"
pciBridge5.pciSlotNumber = "22"
pciBridge6.pciSlotNumber = "23"
pciBridge7.pciSlotNumber = "24"
scsi0.pciSlotNumber = "160"
usb.pciSlotNumber = "32"
ethernet0.pciSlotNumber = "192"
sound.pciSlotNumber = "33"
vmci0.pciSlotNumber = "35"
sata0.pciSlotNumber = "36"
floppy0.present = "FALSE"
guestOS = "other3xlinux-64"
hpet0.present = "TRUE"
sata0.present = "TRUE"
sata0:1.present = "TRUE"
sata0:1.fileName = "{{.ISO}}"
sata0:1.deviceType = "cdrom-image"
{{ if .ConfigDriveURL }}
sata0:2.present = "TRUE"
sata0:2.fileName = "{{.ConfigDriveISO}}"
sata0:2.deviceType = "cdrom-image"
{{ end }}
vmci0.present = "TRUE"
mem.hotadd = "TRUE"
memsize = "{{.Memory}}"
powerType.powerOff = "soft"
powerType.powerOn = "soft"
powerType.reset = "soft"
powerType.suspend = "soft"
scsi0.present = "TRUE"
scsi0.virtualDev = "pvscsi"
scsi0:0.fileName = "{{.MachineName}}.vmdk"
scsi0:0.present = "TRUE"
tools.synctime = "TRUE"
virtualHW.productCompatibility = "hosted"
virtualHW.version = "10"
msg.autoanswer = "TRUE"
uuid.action = "create"
numvcpus = "{{.CPU}}"
hgfs.mapRootShare = "FALSE"
hgfs.linkRootShare = "FALSE"
`
<|endoftext|>


<commit_before>package survey

import (
	"database/sql"
	"errors"
	"github.com/curt-labs/GoSurvey/helpers/database"
	_ "github.com/go-sql-driver/mysql"
	"time"
)

var (
	insertUser = `insert into SurveyUser(fname, lname, email)
								values(?,?,?)`
	deleteUser       = `delete from SurveyUser where id = ?`
	insertUserAnswer = `insert into SurveyUserAnswer(userID, surveyID, questionID, answer)
									values(?,?,?,?)`
	getAllSubmissions = `select su.id, su.fname, su.lname, su.email, su.date_added,
												sua.answer, sq.question, sua.date_answered, sua.surveyID
												from SurveyUserAnswer sua
												join SurveyUser as su on sua.userID = su.id
												join SurveyQuestion as sq on sua.questionID = sq.id
												order by su.date_added desc
												limit ?,?`
	getAllSubmissionsBySurvey = `select su.id, su.fname, su.lname, su.email, su.date_added,
												sua.answer, sq.question, sua.date_answered, sua.surveyID
												from SurveyUserAnswer sua
												join SurveyUser as su on sua.userID = su.id
												join SurveyQuestion as sq on sua.questionID = sq.id
												where sua.surveyID = ?
												order by su.date_added desc
												limit ?,?`
	getSubmissionCount         = `select count(id) as count from SurveyUser`
	getSubmissionCountBySurvey = `select count(distinct su.id) as count from SurveyUser as su
																join SurveyUserAnswer as sua on su.id = sua.userID
																where sua.surveyID = ?`
)

const (
	TIME_LAYOUT = "2006-01-02 15:04:05 MST"
)

type SurveySubmission struct {
	ID        int                      `json:"id"`
	User      SurveyUser               `json:"user"`
	Questions []SurveySubmissionAnswer `json:"questions"`
	Survey    Survey                   `json:"survey"`
}

type SurveyUser struct {
	ID        int       `json:"id"`
	FirstName string    `json:"fname"`
	LastName  string    `json:"lname"`
	Email     string    `json:"email"`
	DateAdded time.Time `json:"date_added"`
}

type SurveySubmissionAnswer struct {
	ID           int       `json:"id"`
	Answer       string    `json:"answer"`
	Question     string    `json:"question"`
	DateAnswered time.Time `json:"date_answered"`
}

func GetAllSubmissions(skip, take, surveyID int) ([]SurveySubmission, error) {
	if take == 0 {
		take = 25
	}

	if skip > 0 {
		skip = (skip - 1) * take
	}

	submissions := make([]SurveySubmission, 0)
	var err error

	db, err := sql.Open("mysql", database.ConnectionString())
	if err != nil {
		return submissions, err
	}
	defer db.Close()

	var stmt *sql.Stmt
	if surveyID == 0 {
		stmt, err = db.Prepare(getAllSubmissions)
	} else {
		stmt, err = db.Prepare(getAllSubmissionsBySurvey)
	}

	if err != nil {
		return submissions, err
	}
	defer stmt.Close()

	var res *sql.Rows
	if surveyID == 0 {
		res, err = stmt.Query(skip, take)
	} else {
		res, err = stmt.Query(surveyID, skip, take)
	}
	if err != nil {
		return submissions, err
	}

	indexedSubmissions := make(map[int]SurveySubmission, 0)

	for res.Next() {
		var ans SurveySubmissionAnswer
		var user SurveyUser
		var s Survey

		if err = res.Scan(&user.ID, &user.FirstName, &user.LastName, &user.Email, &user.DateAdded, &ans.Answer, &ans.Question, &ans.DateAnswered, &s.ID); err == nil {
			if sm, _ := indexedSubmissions[user.ID]; sm.ID == 0 {

				ss := SurveySubmission{
					ID:   user.ID,
					User: user,
				}

				if err := s.Get(); err == nil {
					ss.Survey = s
				}
				indexedSubmissions[user.ID] = ss
			}

			sb := indexedSubmissions[user.ID]
			sb.Questions = append(sb.Questions, ans)
			indexedSubmissions[user.ID] = sb
		}
	}

	for _, sb := range indexedSubmissions {
		submissions = append(submissions, sb)
	}

	return submissions, nil
}

func SubmissionCount(surveyID int) int {
	var err error

	db, err := sql.Open("mysql", database.ConnectionString())
	if err != nil {
		return 0
	}

	defer db.Close()

	var stmt *sql.Stmt
	if surveyID == 0 {
		stmt, err = db.Prepare(getSubmissionCount)
	} else {
		stmt, err = db.Prepare(getSubmissionCountBySurvey)
	}

	if err != nil {
		return 0
	}

	defer stmt.Close()

	var total int
	if surveyID == 0 {
		stmt.QueryRow().Scan(&total)
	} else {
		stmt.QueryRow(surveyID).Scan(&total)
	}

	return total
}

func (s *SurveySubmission) Submit() error {

	if len(s.Questions) == 0 {
		return errors.New("cannot submit a survey without answers")
	}

	surv := Survey{
		ID: s.ID,
	}

	if err := surv.Get(); err != nil {
		return err
	}

	if err := s.User.save(); err != nil {
		return err
	}

	ch := make(chan error)

	for _, question := range s.Questions {
		go func(ss *SurveySubmission, q SurveySubmissionAnswer) {
			ch <- q.save(ss.User.ID, ss.ID)
		}(s, question)
	}

	for _, _ = range s.Questions {
		if err := <-ch; err != nil {
			s.User.delete()
			return err
		}
	}

	return nil
}

func (s *SurveyUser) save() error {
	if s.FirstName == "" {
		return errors.New("first name cannot be blank")
	}
	if s.LastName == "" {
		return errors.New("last name cannot be blank")
	}
	if s.Email == "" {
		return errors.New("e-mail name cannot be blank")
	}

	db, err := sql.Open("mysql", database.ConnectionString())
	if err != nil {
		return err
	}
	defer db.Close()

	stmt, err := db.Prepare(insertUser)
	if err != nil {
		return err
	}
	defer stmt.Close()

	res, err := stmt.Exec(s.FirstName, s.LastName, s.Email)
	if err != nil {
		return err
	}

	id, err := res.LastInsertId()
	if err != nil {
		return err
	}

	s.ID = int(id)

	return nil
}

func (s *SurveySubmissionAnswer) save(userID, surveyID int) error {
	if s.Answer == "" {
		return errors.New("answers cannot be blank")
	}
	if userID == 0 {
		return errors.New("failed to set name and/or email")
	}
	if s.ID == 0 {
		return errors.New("failed to assign question")
	}
	if surveyID == 0 {
		return errors.New("failed to assign survey")
	}

	db, err := sql.Open("mysql", database.ConnectionString())
	if err != nil {
		return err
	}
	defer db.Close()

	stmt, err := db.Prepare(insertUserAnswer)
	if err != nil {
		return err
	}
	defer stmt.Close()

	_, err = stmt.Exec(userID, surveyID, s.ID, s.Answer)
	return err
}

func (s *SurveyUser) delete() error {
	db, err := sql.Open("mysql", database.ConnectionString())
	if err != nil {
		return err
	}
	defer db.Close()

	stmt, err := db.Prepare(deleteUser)
	if err != nil {
		return err
	}
	defer stmt.Close()

	_, err = stmt.Exec(s.ID)
	return err
}
<commit_msg>added get submission<commit_after>package survey

import (
	"database/sql"
	"errors"
	"github.com/curt-labs/GoSurvey/helpers/database"
	_ "github.com/go-sql-driver/mysql"
	"time"
)

var (
	insertUser = `insert into SurveyUser(fname, lname, email)
								values(?,?,?)`
	deleteUser       = `delete from SurveyUser where id = ?`
	insertUserAnswer = `insert into SurveyUserAnswer(userID, surveyID, questionID, answer)
									values(?,?,?,?)`
	getAllSubmissions = `select su.id, su.fname, su.lname, su.email, su.date_added,
												sua.answer, sq.question, sua.date_answered, sua.surveyID
												from SurveyUserAnswer sua
												join SurveyUser as su on sua.userID = su.id
												join SurveyQuestion as sq on sua.questionID = sq.id
												order by su.date_added desc
												limit ?,?`
	getAllSubmissionsBySurvey = `select su.id, su.fname, su.lname, su.email, su.date_added,
												sua.answer, sq.question, sua.date_answered, sua.surveyID
												from SurveyUserAnswer sua
												join SurveyUser as su on sua.userID = su.id
												join SurveyQuestion as sq on sua.questionID = sq.id
												where sua.surveyID = ?
												order by su.date_added desc
												limit ?,?`
	getSubmissionCount         = `select count(id) as count from SurveyUser`
	getSubmissionCountBySurvey = `select count(distinct su.id) as count from SurveyUser as su
																join SurveyUserAnswer as sua on su.id = sua.userID
																where sua.surveyID = ?`
	getSubmission = `select su.id, su.fname, su.lname, su.email, su.date_added,
												sua.answer, sq.question, sua.date_answered, sua.surveyID
												from SurveyUserAnswer sua
												join SurveyUser as su on sua.userID = su.id
												join SurveyQuestion as sq on sua.questionID = sq.id
												where su.id = ?`
)

const (
	TIME_LAYOUT = "2006-01-02 15:04:05 MST"
)

type SurveySubmission struct {
	ID        int                      `json:"id"`
	User      SurveyUser               `json:"user"`
	Questions []SurveySubmissionAnswer `json:"questions"`
	Survey    Survey                   `json:"survey"`
}

type SurveyUser struct {
	ID        int       `json:"id"`
	FirstName string    `json:"fname"`
	LastName  string    `json:"lname"`
	Email     string    `json:"email"`
	DateAdded time.Time `json:"date_added"`
}

type SurveySubmissionAnswer struct {
	ID           int       `json:"id"`
	Answer       string    `json:"answer"`
	Question     string    `json:"question"`
	DateAnswered time.Time `json:"date_answered"`
}

func GetAllSubmissions(skip, take, surveyID int) ([]SurveySubmission, error) {
	if take == 0 {
		take = 25
	}

	if skip > 0 {
		skip = (skip - 1) * take
	}

	submissions := make([]SurveySubmission, 0)
	var err error

	db, err := sql.Open("mysql", database.ConnectionString())
	if err != nil {
		return submissions, err
	}
	defer db.Close()

	var stmt *sql.Stmt
	if surveyID == 0 {
		stmt, err = db.Prepare(getAllSubmissions)
	} else {
		stmt, err = db.Prepare(getAllSubmissionsBySurvey)
	}

	if err != nil {
		return submissions, err
	}
	defer stmt.Close()

	var res *sql.Rows
	if surveyID == 0 {
		res, err = stmt.Query(skip, take)
	} else {
		res, err = stmt.Query(surveyID, skip, take)
	}
	if err != nil {
		return submissions, err
	}

	indexedSubmissions := make(map[int]SurveySubmission, 0)

	for res.Next() {
		var ans SurveySubmissionAnswer
		var user SurveyUser
		var s Survey

		if err = res.Scan(&user.ID, &user.FirstName, &user.LastName, &user.Email, &user.DateAdded, &ans.Answer, &ans.Question, &ans.DateAnswered, &s.ID); err == nil {
			if sm, _ := indexedSubmissions[user.ID]; sm.ID == 0 {

				ss := SurveySubmission{
					ID:   user.ID,
					User: user,
				}

				if err := s.Get(); err == nil {
					ss.Survey = s
				}
				indexedSubmissions[user.ID] = ss
			}

			sb := indexedSubmissions[user.ID]
			sb.Questions = append(sb.Questions, ans)
			indexedSubmissions[user.ID] = sb
		}
	}

	for _, sb := range indexedSubmissions {
		submissions = append(submissions, sb)
	}

	return submissions, nil
}

func SubmissionCount(surveyID int) int {
	var err error

	db, err := sql.Open("mysql", database.ConnectionString())
	if err != nil {
		return 0
	}

	defer db.Close()

	var stmt *sql.Stmt
	if surveyID == 0 {
		stmt, err = db.Prepare(getSubmissionCount)
	} else {
		stmt, err = db.Prepare(getSubmissionCountBySurvey)
	}

	if err != nil {
		return 0
	}

	defer stmt.Close()

	var total int
	if surveyID == 0 {
		stmt.QueryRow().Scan(&total)
	} else {
		stmt.QueryRow(surveyID).Scan(&total)
	}

	return total
}

func (s *SurveySubmission) Get() error {

	var err error

	db, err := sql.Open("mysql", database.ConnectionString())
	if err != nil {
		return err
	}
	defer db.Close()

	stmt, err := db.Prepare(getSubmission)
	if err != nil {
		return err
	}
	defer stmt.Close()

	res, err := stmt.Query(s.ID)
	if err != nil {
		return err
	}

	for res.Next() {
		var ans SurveySubmissionAnswer
		var sur Survey

		if err = res.Scan(&s.User.ID, &s.User.FirstName, &s.User.LastName, &s.User.Email, &s.User.DateAdded, &ans.Answer, &ans.Question, &ans.DateAnswered, &sur.ID); err == nil {

			if sur.Name == "" {
				if err := sur.Get(); err == nil {
					s.Survey = sur
				}
			}

			s.Questions = append(s.Questions, ans)
		}
	}

	return nil
}

func (s *SurveySubmission) Submit() error {

	if len(s.Questions) == 0 {
		return errors.New("cannot submit a survey without answers")
	}

	surv := Survey{
		ID: s.ID,
	}

	if err := surv.Get(); err != nil {
		return err
	}

	if err := s.User.save(); err != nil {
		return err
	}

	ch := make(chan error)

	for _, question := range s.Questions {
		go func(ss *SurveySubmission, q SurveySubmissionAnswer) {
			ch <- q.save(ss.User.ID, ss.ID)
		}(s, question)
	}

	for _, _ = range s.Questions {
		if err := <-ch; err != nil {
			s.User.delete()
			return err
		}
	}

	return nil
}

func (s *SurveyUser) save() error {
	if s.FirstName == "" {
		return errors.New("first name cannot be blank")
	}
	if s.LastName == "" {
		return errors.New("last name cannot be blank")
	}
	if s.Email == "" {
		return errors.New("e-mail name cannot be blank")
	}

	db, err := sql.Open("mysql", database.ConnectionString())
	if err != nil {
		return err
	}
	defer db.Close()

	stmt, err := db.Prepare(insertUser)
	if err != nil {
		return err
	}
	defer stmt.Close()

	res, err := stmt.Exec(s.FirstName, s.LastName, s.Email)
	if err != nil {
		return err
	}

	id, err := res.LastInsertId()
	if err != nil {
		return err
	}

	s.ID = int(id)

	return nil
}

func (s *SurveySubmissionAnswer) save(userID, surveyID int) error {
	if s.Answer == "" {
		return errors.New("answers cannot be blank")
	}
	if userID == 0 {
		return errors.New("failed to set name and/or email")
	}
	if s.ID == 0 {
		return errors.New("failed to assign question")
	}
	if surveyID == 0 {
		return errors.New("failed to assign survey")
	}

	db, err := sql.Open("mysql", database.ConnectionString())
	if err != nil {
		return err
	}
	defer db.Close()

	stmt, err := db.Prepare(insertUserAnswer)
	if err != nil {
		return err
	}
	defer stmt.Close()

	_, err = stmt.Exec(userID, surveyID, s.ID, s.Answer)
	return err
}

func (s *SurveyUser) delete() error {
	db, err := sql.Open("mysql", database.ConnectionString())
	if err != nil {
		return err
	}
	defer db.Close()

	stmt, err := db.Prepare(deleteUser)
	if err != nil {
		return err
	}
	defer stmt.Close()

	_, err = stmt.Exec(s.ID)
	return err
}
<|endoftext|>


<commit_before>package config

import (
	"fmt"
	"regexp"
)

type RawGit struct {
	RawGitExport         `yaml:",inline"`
	As                   string                `yaml:"as,omitempty"`
	Url                  string                `yaml:"url,omitempty"`
	Branch               string                `yaml:"branch,omitempty"`
	Commit               string                `yaml:"commit,omitempty"`
	RawStageDependencies *RawStageDependencies `yaml:"stageDependencies,omitempty"`

	RawDimg *RawDimg `yaml:"-"` // parent

	UnsupportedAttributes map[string]interface{} `yaml:",inline"`
}

func (c *RawGit) ConfigSection() interface{} {
	return c
}

func (c *RawGit) Doc() *Doc {
	return c.RawDimg.Doc
}

func (c *RawGit) Type() string {
	if c.Url != "" {
		return "remote"
	}
	return "local"
}

func (c *RawGit) UnmarshalYAML(unmarshal func(interface{}) error) error {
	c.RawGitExport.RawExportBase = NewRawExportBase()
	if parent, ok := ParentStack.Peek().(*RawDimg); ok {
		c.RawDimg = parent
	}

	ParentStack.Push(c)
	type plain RawGit
	err := unmarshal((*plain)(c))
	ParentStack.Pop()
	if err != nil {
		return err
	}

	c.RawGitExport.InlinedIntoRaw(c)

	if err := CheckOverflow(c.UnsupportedAttributes, c, c.RawDimg.Doc); err != nil {
		return err
	}

	return nil
}

func (c *RawGit) ToGitLocalDirective() (gitLocal *GitLocal, err error) {
	gitLocal = &GitLocal{}

	if gitLocalExport, err := c.ToGitLocalExportDirective(); err != nil {
		return nil, err
	} else {
		gitLocal.GitLocalExport = gitLocalExport
	}

	gitLocal.As = c.As

	gitLocal.Raw = c

	if err := c.ValidateGitLocalDirective(gitLocal); err != nil {
		return nil, err
	}

	return gitLocal, nil
}

func (c *RawGit) ValidateGitLocalDirective(gitLocal *GitLocal) (err error) {
	if err := gitLocal.Validate(); err != nil {
		return err
	}

	return nil
}

func (c *RawGit) ToGitLocalExportDirective() (gitLocalExport *GitLocalExport, err error) {
	gitLocalExport = &GitLocalExport{}

	gitLocalExport.GitExportBase = &GitExportBase{}
	if gitExport, err := c.RawGitExport.ToDirective(); err != nil {
		return nil, err
	} else {
		gitLocalExport.GitExportBase.GitExport = gitExport
	}

	if c.RawStageDependencies != nil {
		if stageDependencies, err := c.RawStageDependencies.ToDirective(); err != nil {
			return nil, err
		} else {
			gitLocalExport.StageDependencies = stageDependencies
		}
	}

	gitLocalExport.Raw = c

	if err := c.ValidateGitLocalExportDirective(gitLocalExport); err != nil {
		return nil, err
	}

	return gitLocalExport, nil
}

func (c *RawGit) ValidateGitLocalExportDirective(gitLocalExport *GitLocalExport) (err error) {
	if err := gitLocalExport.Validate(); err != nil {
		return err
	}

	return nil
}

func (c *RawGit) ToGitRemoteDirective() (gitRemote *GitRemote, err error) {
	gitRemote = &GitRemote{}

	if gitRemoteExport, err := c.ToGitRemoteExportDirective(); err != nil {
		return nil, err
	} else {
		gitRemote.GitRemoteExport = gitRemoteExport
	}

	gitRemote.As = c.As
	gitRemote.Url = c.Url

	r := regexp.MustCompile(`.*?([^/ ]+/[^/ ]+)(\.git)?`)
	match := r.FindStringSubmatch(c.Url)
	if len(match) == 3 {
		gitRemote.Name = match[1]
	} else {
		return nil, fmt.Errorf("Cannot determine repo name from `url: %s`: url is not fit `.*?([^/ ]+/[^/ ]+)(.git)?` regex!\n\n%s\n%s", c.Url, DumpConfigSection(c), DumpConfigDoc(c.RawDimg.Doc))
	}

	gitRemote.Raw = c

	if err := c.ValidateGitRemoteDirective(gitRemote); err != nil {
		return nil, err
	}

	return gitRemote, nil
}

func (c *RawGit) ValidateGitRemoteDirective(gitRemote *GitRemote) (err error) {
	if err := gitRemote.Validate(); err != nil {
		return err
	}

	return nil
}

func (c *RawGit) ToGitRemoteExportDirective() (gitRemoteExport *GitRemoteExport, err error) {
	gitRemoteExport = &GitRemoteExport{}

	if gitLocalExport, err := c.ToGitLocalExportDirective(); err != nil {
		return nil, err
	} else {
		gitRemoteExport.GitLocalExport = gitLocalExport
	}

	gitRemoteExport.Branch = c.Branch
	gitRemoteExport.Commit = c.Commit

	gitRemoteExport.Raw = c

	if err := c.ValidateGitRemoteExportDirective(gitRemoteExport); err != nil {
		return nil, err
	}

	return gitRemoteExport, nil
}

func (c *RawGit) ValidateGitRemoteExportDirective(gitRemoteExport *GitRemoteExport) (err error) {
	if err := gitRemoteExport.Validate(); err != nil {
		return err
	}

	return nil
}
<commit_msg>Config: fix git remote name from url regex<commit_after>package config

import (
	"fmt"
	"regexp"
)

type RawGit struct {
	RawGitExport         `yaml:",inline"`
	As                   string                `yaml:"as,omitempty"`
	Url                  string                `yaml:"url,omitempty"`
	Branch               string                `yaml:"branch,omitempty"`
	Commit               string                `yaml:"commit,omitempty"`
	RawStageDependencies *RawStageDependencies `yaml:"stageDependencies,omitempty"`

	RawDimg *RawDimg `yaml:"-"` // parent

	UnsupportedAttributes map[string]interface{} `yaml:",inline"`
}

func (c *RawGit) ConfigSection() interface{} {
	return c
}

func (c *RawGit) Doc() *Doc {
	return c.RawDimg.Doc
}

func (c *RawGit) Type() string {
	if c.Url != "" {
		return "remote"
	}
	return "local"
}

func (c *RawGit) UnmarshalYAML(unmarshal func(interface{}) error) error {
	c.RawGitExport.RawExportBase = NewRawExportBase()
	if parent, ok := ParentStack.Peek().(*RawDimg); ok {
		c.RawDimg = parent
	}

	ParentStack.Push(c)
	type plain RawGit
	err := unmarshal((*plain)(c))
	ParentStack.Pop()
	if err != nil {
		return err
	}

	c.RawGitExport.InlinedIntoRaw(c)

	if err := CheckOverflow(c.UnsupportedAttributes, c, c.RawDimg.Doc); err != nil {
		return err
	}

	return nil
}

func (c *RawGit) ToGitLocalDirective() (gitLocal *GitLocal, err error) {
	gitLocal = &GitLocal{}

	if gitLocalExport, err := c.ToGitLocalExportDirective(); err != nil {
		return nil, err
	} else {
		gitLocal.GitLocalExport = gitLocalExport
	}

	gitLocal.As = c.As

	gitLocal.Raw = c

	if err := c.ValidateGitLocalDirective(gitLocal); err != nil {
		return nil, err
	}

	return gitLocal, nil
}

func (c *RawGit) ValidateGitLocalDirective(gitLocal *GitLocal) (err error) {
	if err := gitLocal.Validate(); err != nil {
		return err
	}

	return nil
}

func (c *RawGit) ToGitLocalExportDirective() (gitLocalExport *GitLocalExport, err error) {
	gitLocalExport = &GitLocalExport{}

	gitLocalExport.GitExportBase = &GitExportBase{}
	if gitExport, err := c.RawGitExport.ToDirective(); err != nil {
		return nil, err
	} else {
		gitLocalExport.GitExportBase.GitExport = gitExport
	}

	if c.RawStageDependencies != nil {
		if stageDependencies, err := c.RawStageDependencies.ToDirective(); err != nil {
			return nil, err
		} else {
			gitLocalExport.StageDependencies = stageDependencies
		}
	}

	gitLocalExport.Raw = c

	if err := c.ValidateGitLocalExportDirective(gitLocalExport); err != nil {
		return nil, err
	}

	return gitLocalExport, nil
}

func (c *RawGit) ValidateGitLocalExportDirective(gitLocalExport *GitLocalExport) (err error) {
	if err := gitLocalExport.Validate(); err != nil {
		return err
	}

	return nil
}

func (c *RawGit) ToGitRemoteDirective() (gitRemote *GitRemote, err error) {
	gitRemote = &GitRemote{}

	if gitRemoteExport, err := c.ToGitRemoteExportDirective(); err != nil {
		return nil, err
	} else {
		gitRemote.GitRemoteExport = gitRemoteExport
	}

	gitRemote.As = c.As
	gitRemote.Url = c.Url

	r := regexp.MustCompile(`.*?([^/ ]+/[^/ ]+)(\.git?)$`)
	match := r.FindStringSubmatch(c.Url)
	if len(match) == 3 {
		gitRemote.Name = match[1]
	} else {
		return nil, fmt.Errorf("Cannot determine repo name from `url: %s`: url is not fit `.*?([^/ ]+/[^/ ]+)(.git)?` regex!\n\n%s\n%s", c.Url, DumpConfigSection(c), DumpConfigDoc(c.RawDimg.Doc))
	}

	gitRemote.Raw = c

	if err := c.ValidateGitRemoteDirective(gitRemote); err != nil {
		return nil, err
	}

	return gitRemote, nil
}

func (c *RawGit) ValidateGitRemoteDirective(gitRemote *GitRemote) (err error) {
	if err := gitRemote.Validate(); err != nil {
		return err
	}

	return nil
}

func (c *RawGit) ToGitRemoteExportDirective() (gitRemoteExport *GitRemoteExport, err error) {
	gitRemoteExport = &GitRemoteExport{}

	if gitLocalExport, err := c.ToGitLocalExportDirective(); err != nil {
		return nil, err
	} else {
		gitRemoteExport.GitLocalExport = gitLocalExport
	}

	gitRemoteExport.Branch = c.Branch
	gitRemoteExport.Commit = c.Commit

	gitRemoteExport.Raw = c

	if err := c.ValidateGitRemoteExportDirective(gitRemoteExport); err != nil {
		return nil, err
	}

	return gitRemoteExport, nil
}

func (c *RawGit) ValidateGitRemoteExportDirective(gitRemoteExport *GitRemoteExport) (err error) {
	if err := gitRemoteExport.Validate(); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package dialer

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

	"github.com/rancher/norman/types/slice"
	"github.com/rancher/rancher/pkg/tunnelserver"
	"github.com/rancher/remotedialer"
	"github.com/rancher/rke/k8s"
	"github.com/rancher/rke/services"
	v3 "github.com/rancher/types/apis/management.cattle.io/v3"
	"github.com/rancher/types/config"
	"github.com/rancher/types/config/dialer"
	"github.com/sirupsen/logrus"
	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/labels"
)

func NewFactory(apiContext *config.ScaledContext) (*Factory, error) {
	authorizer := tunnelserver.NewAuthorizer(apiContext)
	tunneler := tunnelserver.NewTunnelServer(authorizer)

	return &Factory{
		clusterLister:    apiContext.Management.Clusters("").Controller().Lister(),
		nodeLister:       apiContext.Management.Nodes("").Controller().Lister(),
		TunnelServer:     tunneler,
		TunnelAuthorizer: authorizer,
	}, nil
}

type Factory struct {
	nodeLister       v3.NodeLister
	clusterLister    v3.ClusterLister
	TunnelServer     *remotedialer.Server
	TunnelAuthorizer *tunnelserver.Authorizer
}

func (f *Factory) ClusterDialer(clusterName string) (dialer.Dialer, error) {
	return func(network, address string) (net.Conn, error) {
		d, err := f.clusterDialer(clusterName, address)
		if err != nil {
			return nil, err
		}
		return d(network, address)
	}, nil
}

func isCloudDriver(cluster *v3.Cluster) bool {
	return !cluster.Spec.Internal && cluster.Status.Driver != v3.ClusterDriverImported && cluster.Status.Driver != v3.ClusterDriverRKE
}

func (f *Factory) translateClusterAddress(cluster *v3.Cluster, clusterHostPort, address string) string {
	if clusterHostPort != address {
		logrus.Debugf("dialerFactory: apiEndpoint clusterHostPort [%s] is not equal to address [%s]", clusterHostPort, address)
		return address
	}

	host, port, err := net.SplitHostPort(address)
	if err != nil {
		return address
	}

	// Make sure that control plane node we are connecting to is not bad, also use internal address
	nodes, err := f.nodeLister.List(cluster.Name, labels.Everything())
	if err != nil {
		return address
	}

	clusterGood := v3.ClusterConditionReady.IsTrue(cluster)
	logrus.Debugf("dialerFactory: ClusterConditionReady for cluster [%s] is [%t]", cluster.Spec.DisplayName, clusterGood)
	lastGoodHost := ""
	logrus.Debug("dialerFactory: finding a node to tunnel the cluster connection")
	for _, node := range nodes {
		var (
			publicIP  = node.Status.NodeAnnotations[k8s.ExternalAddressAnnotation]
			privateIP = node.Status.NodeAnnotations[k8s.InternalAddressAnnotation]
		)

		fakeNode := &v1.Node{
			Status: node.Status.InternalNodeStatus,
		}

		nodeGood := v3.NodeConditionRegistered.IsTrue(node) && v3.NodeConditionProvisioned.IsTrue(node) &&
			!v3.NodeConditionReady.IsUnknown(fakeNode) && node.DeletionTimestamp == nil

		if !nodeGood {
			logrus.Debugf("dialerFactory: Skipping node [%s] for tunneling the cluster connection because nodeConditions are not as expected", node.Spec.RequestedHostname)
			logrus.Debugf("dialerFactory: Node conditions for node [%s]: %+v", node.Status.NodeName, node.Status.Conditions)
			continue
		}
		if privateIP == "" {
			logrus.Debugf("dialerFactory: Skipping node [%s] for tunneling the cluster connection because privateIP is empty", node.Status.NodeName)
			continue
		}

		logrus.Debugf("dialerFactory: IP addresses for node [%s]: publicIP [%s], privateIP [%s]", node.Status.NodeName, publicIP, privateIP)

		if publicIP == host {
			logrus.Debugf("dialerFactory: publicIP [%s] for node [%s] matches apiEndpoint host [%s], checking if cluster condition Ready is True", publicIP, node.Status.NodeName, host)
			if clusterGood {
				logrus.Debug("dialerFactory: cluster condition Ready is True")
				host = privateIP
				logrus.Debugf("dialerFactory: Using privateIP [%s] of node [%s] as node to tunnel the cluster connection", privateIP, node.Status.NodeName)
				return fmt.Sprintf("%s:%s", host, port)
			}
			logrus.Debug("dialerFactory: cluster condition Ready is False")
		} else if node.Status.NodeConfig != nil && slice.ContainsString(node.Status.NodeConfig.Role, services.ControlRole) {
			logrus.Debugf("dialerFactory: setting node [%s] with privateIP [%s] as option for the connection as it is a controlplane node", node.Status.NodeName, privateIP)
			lastGoodHost = privateIP
		}
	}

	if lastGoodHost != "" {
		logrus.Debugf("dialerFactory: returning [%s:%s] as last good option to tunnel the cluster connection", lastGoodHost, port)
		return fmt.Sprintf("%s:%s", lastGoodHost, port)
	}

	logrus.Debugf("dialerFactory: returning [%s], as no good option was found (no match with apiEndpoint or a controlplane node with correct conditions", address)
	return address
}

func (f *Factory) clusterDialer(clusterName, address string) (dialer.Dialer, error) {
	cluster, err := f.clusterLister.Get("", clusterName)
	if err != nil {
		return nil, err
	}

	if cluster.Spec.Internal {
		// For local (embedded, or import) we just assume we can connect directly
		return native()
	}

	hostPort := hostPort(cluster)
	logrus.Debugf("dialerFactory: apiEndpoint hostPort for cluster [%s] is [%s]", clusterName, hostPort)
	if address == hostPort && isCloudDriver(cluster) {
		// For cloud drivers we just connect directly to the k8s API, not through the tunnel.  All other go through tunnel
		return native()
	}

	if f.TunnelServer.HasSession(cluster.Name) {
		logrus.Debugf("dialerFactory: tunnel session found for cluster [%s]", cluster.Name)
		cd := f.TunnelServer.Dialer(cluster.Name, 15*time.Second)
		return func(network, address string) (net.Conn, error) {
			if cluster.Status.Driver == v3.ClusterDriverRKE {
				address = f.translateClusterAddress(cluster, hostPort, address)
			}
			logrus.Debugf("dialerFactory: returning network [%s] and address [%s] as clusterDialer", network, address)
			return cd(network, address)
		}, nil
	}
	logrus.Debugf("dialerFactory: no tunnel session found for cluster [%s], falling back to nodeDialer", cluster.Name)

	// Try to connect to a node for the cluster dialer
	nodes, err := f.nodeLister.List(cluster.Name, labels.Everything())
	if err != nil {
		return nil, err
	}

	var localAPIEndpoint bool
	if cluster.Status.Driver == v3.ClusterDriverRKE {
		localAPIEndpoint = true
	}

	for _, node := range nodes {
		if node.DeletionTimestamp == nil && v3.NodeConditionProvisioned.IsTrue(node) {
			logrus.Debugf("dialerFactory: using node [%s]/[%s] for nodeDialer",
				node.Labels["management.cattle.io/nodename"], node.Name)
			if nodeDialer, err := f.nodeDialer(clusterName, node.Name); err == nil {
				return func(network, address string) (net.Conn, error) {
					if address == hostPort && localAPIEndpoint {
						logrus.Debug("dialerFactory: rewriting address/port to 127.0.0.1:6443 as node may not" +
							" have direct kube-api access")
						// The node dialer may not have direct access to kube-api so we hit localhost:6443 instead
						address = "127.0.0.1:6443"
					}
					logrus.Debugf("dialerFactory: Returning network [%s] and address [%s] as nodeDialer", network, address)
					return nodeDialer(network, address)
				}, nil
			}
		}
	}

	return nil, fmt.Errorf("waiting for cluster agent to connect")
}

func hostPort(cluster *v3.Cluster) string {
	u, err := url.Parse(cluster.Status.APIEndpoint)
	if err != nil {
		return ""
	}

	if strings.Contains(u.Host, ":") {
		return u.Host
	}
	return u.Host + ":443"
}

func native() (dialer.Dialer, error) {
	netDialer := net.Dialer{
		Timeout:   30 * time.Second,
		KeepAlive: 30 * time.Second,
	}
	return netDialer.Dial, nil
}

func (f *Factory) DockerDialer(clusterName, machineName string) (dialer.Dialer, error) {
	machine, err := f.nodeLister.Get(clusterName, machineName)
	if err != nil {
		return nil, err
	}

	sessionKey := machineSessionKey(machine)
	if f.TunnelServer.HasSession(sessionKey) {
		network, address := "unix", "/var/run/docker.sock"
		if machine.Status.InternalNodeStatus.NodeInfo.OperatingSystem == "windows" {
			network, address = "npipe", "//./pipe/docker_engine"
		}
		d := f.TunnelServer.Dialer(sessionKey, 15*time.Second)
		return func(string, string) (net.Conn, error) {
			return d(network, address)
		}, nil
	}

	return nil, fmt.Errorf("can not build dialer to [%s:%s]", clusterName, machineName)
}

func (f *Factory) NodeDialer(clusterName, machineName string) (dialer.Dialer, error) {
	return func(network, address string) (net.Conn, error) {
		d, err := f.nodeDialer(clusterName, machineName)
		if err != nil {
			return nil, err
		}
		return d(network, address)
	}, nil
}

func (f *Factory) nodeDialer(clusterName, machineName string) (dialer.Dialer, error) {
	machine, err := f.nodeLister.Get(clusterName, machineName)
	if err != nil {
		return nil, err
	}

	sessionKey := machineSessionKey(machine)
	if f.TunnelServer.HasSession(sessionKey) {
		d := f.TunnelServer.Dialer(sessionKey, 15*time.Second)
		return dialer.Dialer(d), nil
	}

	return nil, fmt.Errorf("can not build dialer to [%s:%s]", clusterName, machineName)
}

func machineSessionKey(machine *v3.Node) string {
	return fmt.Sprintf("%s:%s", machine.Namespace, machine.Name)
}
<commit_msg>Hosted cluster dialer fix to work with proxy<commit_after>package dialer

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

	"github.com/rancher/norman/types/slice"
	"github.com/rancher/rancher/pkg/tunnelserver"
	"github.com/rancher/remotedialer"
	"github.com/rancher/rke/k8s"
	"github.com/rancher/rke/services"
	v3 "github.com/rancher/types/apis/management.cattle.io/v3"
	"github.com/rancher/types/config"
	"github.com/rancher/types/config/dialer"
	"github.com/sirupsen/logrus"
	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/labels"
)

func NewFactory(apiContext *config.ScaledContext) (*Factory, error) {
	authorizer := tunnelserver.NewAuthorizer(apiContext)
	tunneler := tunnelserver.NewTunnelServer(authorizer)

	return &Factory{
		clusterLister:    apiContext.Management.Clusters("").Controller().Lister(),
		nodeLister:       apiContext.Management.Nodes("").Controller().Lister(),
		TunnelServer:     tunneler,
		TunnelAuthorizer: authorizer,
	}, nil
}

type Factory struct {
	nodeLister       v3.NodeLister
	clusterLister    v3.ClusterLister
	TunnelServer     *remotedialer.Server
	TunnelAuthorizer *tunnelserver.Authorizer
}

func (f *Factory) ClusterDialer(clusterName string) (dialer.Dialer, error) {
	return func(network, address string) (net.Conn, error) {
		d, err := f.clusterDialer(clusterName, address)
		if err != nil {
			return nil, err
		}
		return d(network, address)
	}, nil
}

func isCloudDriver(cluster *v3.Cluster) bool {
	return !cluster.Spec.Internal && cluster.Status.Driver != v3.ClusterDriverImported && cluster.Status.Driver != v3.ClusterDriverRKE
}

func (f *Factory) translateClusterAddress(cluster *v3.Cluster, clusterHostPort, address string) string {
	if clusterHostPort != address {
		logrus.Debugf("dialerFactory: apiEndpoint clusterHostPort [%s] is not equal to address [%s]", clusterHostPort, address)
		return address
	}

	host, port, err := net.SplitHostPort(address)
	if err != nil {
		return address
	}

	// Make sure that control plane node we are connecting to is not bad, also use internal address
	nodes, err := f.nodeLister.List(cluster.Name, labels.Everything())
	if err != nil {
		return address
	}

	clusterGood := v3.ClusterConditionReady.IsTrue(cluster)
	logrus.Debugf("dialerFactory: ClusterConditionReady for cluster [%s] is [%t]", cluster.Spec.DisplayName, clusterGood)
	lastGoodHost := ""
	logrus.Debug("dialerFactory: finding a node to tunnel the cluster connection")
	for _, node := range nodes {
		var (
			publicIP  = node.Status.NodeAnnotations[k8s.ExternalAddressAnnotation]
			privateIP = node.Status.NodeAnnotations[k8s.InternalAddressAnnotation]
		)

		fakeNode := &v1.Node{
			Status: node.Status.InternalNodeStatus,
		}

		nodeGood := v3.NodeConditionRegistered.IsTrue(node) && v3.NodeConditionProvisioned.IsTrue(node) &&
			!v3.NodeConditionReady.IsUnknown(fakeNode) && node.DeletionTimestamp == nil

		if !nodeGood {
			logrus.Debugf("dialerFactory: Skipping node [%s] for tunneling the cluster connection because nodeConditions are not as expected", node.Spec.RequestedHostname)
			logrus.Debugf("dialerFactory: Node conditions for node [%s]: %+v", node.Status.NodeName, node.Status.Conditions)
			continue
		}
		if privateIP == "" {
			logrus.Debugf("dialerFactory: Skipping node [%s] for tunneling the cluster connection because privateIP is empty", node.Status.NodeName)
			continue
		}

		logrus.Debugf("dialerFactory: IP addresses for node [%s]: publicIP [%s], privateIP [%s]", node.Status.NodeName, publicIP, privateIP)

		if publicIP == host {
			logrus.Debugf("dialerFactory: publicIP [%s] for node [%s] matches apiEndpoint host [%s], checking if cluster condition Ready is True", publicIP, node.Status.NodeName, host)
			if clusterGood {
				logrus.Debug("dialerFactory: cluster condition Ready is True")
				host = privateIP
				logrus.Debugf("dialerFactory: Using privateIP [%s] of node [%s] as node to tunnel the cluster connection", privateIP, node.Status.NodeName)
				return fmt.Sprintf("%s:%s", host, port)
			}
			logrus.Debug("dialerFactory: cluster condition Ready is False")
		} else if node.Status.NodeConfig != nil && slice.ContainsString(node.Status.NodeConfig.Role, services.ControlRole) {
			logrus.Debugf("dialerFactory: setting node [%s] with privateIP [%s] as option for the connection as it is a controlplane node", node.Status.NodeName, privateIP)
			lastGoodHost = privateIP
		}
	}

	if lastGoodHost != "" {
		logrus.Debugf("dialerFactory: returning [%s:%s] as last good option to tunnel the cluster connection", lastGoodHost, port)
		return fmt.Sprintf("%s:%s", lastGoodHost, port)
	}

	logrus.Debugf("dialerFactory: returning [%s], as no good option was found (no match with apiEndpoint or a controlplane node with correct conditions", address)
	return address
}

func (f *Factory) clusterDialer(clusterName, address string) (dialer.Dialer, error) {
	cluster, err := f.clusterLister.Get("", clusterName)
	if err != nil {
		return nil, err
	}

	if cluster.Spec.Internal {
		// For local (embedded, or import) we just assume we can connect directly
		return native()
	}

	hostPort := hostPort(cluster)
	logrus.Debugf("dialerFactory: apiEndpoint hostPort for cluster [%s] is [%s]", clusterName, hostPort)
	if (address == hostPort || isProxyAddress(address)) && isCloudDriver(cluster) {
		// For cloud drivers we just connect directly to the k8s API, not through the tunnel.  All other go through tunnel
		return native()
	}

	if f.TunnelServer.HasSession(cluster.Name) {
		logrus.Debugf("dialerFactory: tunnel session found for cluster [%s]", cluster.Name)
		cd := f.TunnelServer.Dialer(cluster.Name, 15*time.Second)
		return func(network, address string) (net.Conn, error) {
			if cluster.Status.Driver == v3.ClusterDriverRKE {
				address = f.translateClusterAddress(cluster, hostPort, address)
			}
			logrus.Debugf("dialerFactory: returning network [%s] and address [%s] as clusterDialer", network, address)
			return cd(network, address)
		}, nil
	}
	logrus.Debugf("dialerFactory: no tunnel session found for cluster [%s], falling back to nodeDialer", cluster.Name)

	// Try to connect to a node for the cluster dialer
	nodes, err := f.nodeLister.List(cluster.Name, labels.Everything())
	if err != nil {
		return nil, err
	}

	var localAPIEndpoint bool
	if cluster.Status.Driver == v3.ClusterDriverRKE {
		localAPIEndpoint = true
	}

	for _, node := range nodes {
		if node.DeletionTimestamp == nil && v3.NodeConditionProvisioned.IsTrue(node) {
			logrus.Debugf("dialerFactory: using node [%s]/[%s] for nodeDialer",
				node.Labels["management.cattle.io/nodename"], node.Name)
			if nodeDialer, err := f.nodeDialer(clusterName, node.Name); err == nil {
				return func(network, address string) (net.Conn, error) {
					if address == hostPort && localAPIEndpoint {
						logrus.Debug("dialerFactory: rewriting address/port to 127.0.0.1:6443 as node may not" +
							" have direct kube-api access")
						// The node dialer may not have direct access to kube-api so we hit localhost:6443 instead
						address = "127.0.0.1:6443"
					}
					logrus.Debugf("dialerFactory: Returning network [%s] and address [%s] as nodeDialer", network, address)
					return nodeDialer(network, address)
				}, nil
			}
		}
	}

	return nil, fmt.Errorf("waiting for cluster agent to connect")
}

func hostPort(cluster *v3.Cluster) string {
	u, err := url.Parse(cluster.Status.APIEndpoint)
	if err != nil {
		return ""
	}

	if strings.Contains(u.Host, ":") {
		return u.Host
	}
	return u.Host + ":443"
}

func native() (dialer.Dialer, error) {
	netDialer := net.Dialer{
		Timeout:   30 * time.Second,
		KeepAlive: 30 * time.Second,
	}
	return netDialer.Dial, nil
}

func (f *Factory) DockerDialer(clusterName, machineName string) (dialer.Dialer, error) {
	machine, err := f.nodeLister.Get(clusterName, machineName)
	if err != nil {
		return nil, err
	}

	sessionKey := machineSessionKey(machine)
	if f.TunnelServer.HasSession(sessionKey) {
		network, address := "unix", "/var/run/docker.sock"
		if machine.Status.InternalNodeStatus.NodeInfo.OperatingSystem == "windows" {
			network, address = "npipe", "//./pipe/docker_engine"
		}
		d := f.TunnelServer.Dialer(sessionKey, 15*time.Second)
		return func(string, string) (net.Conn, error) {
			return d(network, address)
		}, nil
	}

	return nil, fmt.Errorf("can not build dialer to [%s:%s]", clusterName, machineName)
}

func (f *Factory) NodeDialer(clusterName, machineName string) (dialer.Dialer, error) {
	return func(network, address string) (net.Conn, error) {
		d, err := f.nodeDialer(clusterName, machineName)
		if err != nil {
			return nil, err
		}
		return d(network, address)
	}, nil
}

func (f *Factory) nodeDialer(clusterName, machineName string) (dialer.Dialer, error) {
	machine, err := f.nodeLister.Get(clusterName, machineName)
	if err != nil {
		return nil, err
	}

	sessionKey := machineSessionKey(machine)
	if f.TunnelServer.HasSession(sessionKey) {
		d := f.TunnelServer.Dialer(sessionKey, 15*time.Second)
		return dialer.Dialer(d), nil
	}

	return nil, fmt.Errorf("can not build dialer to [%s:%s]", clusterName, machineName)
}

func machineSessionKey(machine *v3.Node) string {
	return fmt.Sprintf("%s:%s", machine.Namespace, machine.Name)
}

func isProxyAddress(address string) bool {
	proxy := getEnvAny("HTTP_PROXY", "http_proxy")
	if proxy == "" {
		proxy = getEnvAny("HTTPS_PROXY", "https_proxy")
	}

	if proxy == "" {
		return false
	}

	parsed, err := parseProxy(proxy)
	if err != nil {
		logrus.Warnf("Failed to parse http_proxy url %s: %v", proxy, err)
		return false
	}
	return parsed.Host == address
}

func getEnvAny(names ...string) string {
	for _, n := range names {
		if val := os.Getenv(n); val != "" {
			return val
		}
	}
	return ""
}

func parseProxy(proxy string) (*url.URL, error) {
	if proxy == "" {
		return nil, nil
	}

	proxyURL, err := url.Parse(proxy)
	if err != nil ||
		(proxyURL.Scheme != "http" &&
			proxyURL.Scheme != "https" &&
			proxyURL.Scheme != "socks5") {
		// proxy was bogus. Try pre-pending "http://" to it and
		// see if that parses correctly. If not, fall through
		if proxyURL, err := url.Parse("http://" + proxy); err == nil {
			return proxyURL, nil
		}
	}
	if err != nil {
		return nil, fmt.Errorf("invalid proxy address %q: %v", proxy, err)
	}
	return proxyURL, nil
}
<|endoftext|>