<commit_before>package converters

import (
	"strconv"
	"time"
)

type IntConverter struct {
	i int64
}

func Int(i int) *IntConverter {
	return Int64(int64(i))
}

func Int64(i int64) *IntConverter {
	return &IntConverter{i: i}
}

func (c *IntConverter) ToTimeFromNsec() time.Time {
	return time.Unix(0, c.i)
}

func (c *IntConverter) ToTimeFromSec() time.Time {
	return time.Unix(c.i, 0)
}

func (c *IntConverter) ToString() string {
	return strconv.FormatInt(c.i, 10)
}
<commit_msg>Added doc for IntConverter<commit_after>package converters

import (
	"strconv"
	"time"
)

// IntConverter is a converter for converting integers to various standard types
// All conversions are done with int64 as base
type IntConverter struct {
	i int64
}

// Int creates an new IntConverter by calling Int64()
func Int(i int) *IntConverter {
	return Int64(int64(i))
}

// Int64 creates an IntConverter
func Int64(i int64) *IntConverter {
	return &IntConverter{i: i}
}

// ToTimeFromNsec converts from nanoseconds to time.Time
// See time.Unix
func (c *IntConverter) ToTimeFromNsec() time.Time {
	return time.Unix(0, c.i)
}

// ToTimeFromSec converts from seconds to time.Time
// See time.Unix
func (c *IntConverter) ToTimeFromSec() time.Time {
	return time.Unix(c.i, 0)
}

// ToString returns a string representation of given integer
func (c *IntConverter) ToString() string {
	return strconv.FormatInt(c.i, 10)
}
<|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) {
	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"))
	}

	t.Run("IngressIP", func(t *testing.T) {
		if HyperVDriver() {
			t.Skip("The test WaitService/IngressIP is broken on hyperv https://github.com/kubernetes/minikube/issues/8381")
		}
		// 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: %v", 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.Output())
		}
	})
}

// 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 tests<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) {
	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 GithubActionRunner() && runtime.GOOS=="darwin" {
		t.Skip("The test WaitService is broken on github actions in macos https://github.com/kubernetes/minikube/issues/8434")
	}
	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"))
	}

	t.Run("IngressIP", func(t *testing.T) {
		if HyperVDriver() {
			t.Skip("The test WaitService/IngressIP is broken on hyperv https://github.com/kubernetes/minikube/issues/8381")
		}
		// 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: %v", 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.Output())
		}
	})
}

// 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")
	}
	if GithubActionRunner() && runtime.GOOS=="darwin" {
		t.Skip("skipping: access direct test is broken on github actions on macos https://github.com/kubernetes/minikube/issues/8434")
	}

	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) {
	if GithubActionRunner() && runtime.GOOS=="darwin" {
		t.Skip("skipping: access direct test is broken on github actions on macos https://github.com/kubernetes/minikube/issues/8434")
	}

	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) {
	if GithubActionRunner() && runtime.GOOS=="darwin" {
		t.Skip("skipping: access direct test is broken on github actions on macos https://github.com/kubernetes/minikube/issues/8434")
	}

	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) {
	if GithubActionRunner() && runtime.GOOS=="darwin" {
		t.Skip("skipping: access direct test is broken on github actions on macos https://github.com/kubernetes/minikube/issues/8434")
	}

	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)
}
<|endoftext|>


<commit_before>// Copyright 2015 CloudAwan LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package sshclient

import (
	"bytes"
	"errors"
	"golang.org/x/crypto/ssh"
	"io"
	"net"
	"strconv"
	"strings"
	"sync"
	"time"
)

// The function ssh.Dial doesn't have timeout mechanism for dial so this function is used
func dialWithTimeout(network, addr string, config *ssh.ClientConfig, timeout time.Duration) (*ssh.Client, error) {
	conn, err := net.DialTimeout(network, addr, timeout)
	if err != nil {
		return nil, err
	}
	c, chans, reqs, err := ssh.NewClientConn(conn, addr, config)
	if err != nil {
		return nil, err
	}
	return ssh.NewClient(c, chans, reqs), nil
}

func InteractiveSSH(dialTimeout time.Duration, sessionTimeout time.Duration, host string, port int, user string, password string,
	commandSlice []string, interactiveMap map[string]string) ([]string, error) {
	sshConfig := &ssh.ClientConfig{
		User: user,
		Auth: []ssh.AuthMethod{
			ssh.Password(password),
		},
	}

	connection, err := dialWithTimeout("tcp", host+":"+strconv.Itoa(port), sshConfig, dialTimeout)
	if err != nil {
		return nil, err
	}
	defer connection.Close()

	session, err := connection.NewSession()
	if err != nil {
		return nil, err
	}
	defer session.Close()

	modes := ssh.TerminalModes{
		ssh.ECHO:          0,     // disable echoing
		ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
		ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
	}
	if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
		return nil, err
	}
	w, err := session.StdinPipe()
	if err != nil {
		return nil, err
	}
	r, err := session.StdoutPipe()
	if err != nil {
		return nil, err
	}
	e, err := session.StderrPipe()
	if err != nil {
		return nil, err
	}

	inputChannel, outputChannel, errorChannel := shell(w, r, e, interactiveMap)
	if err := session.Shell(); err != nil {
		return nil, err
	}

	isTimeout := false
	go func() {
		// Timeout the session to prevent got stuck
		time.Sleep(sessionTimeout)
		isTimeout = true
		session.Close()
	}()

	// Ignore the ssh tty welcome page
	<-outputChannel

	resultSlice := make([]string, 0)
	for _, command := range commandSlice {
		inputChannel <- command
		result, ok := <-outputChannel
		if ok {
			resultSlice = append(resultSlice, result)
		} else {
			break
		}
	}

	// End terminal
	inputChannel <- "exit\n"
	// Close input
	close(inputChannel)

	// Wait until I/O is closed
	session.Wait()

	buffer := bytes.Buffer{}
	for {
		errorMessage, ok := <-errorChannel
		if ok {
			buffer.WriteString(errorMessage)
		} else {
			break
		}
	}

	if isTimeout {
		buffer.WriteString("Session timeout")
	}

	if buffer.Len() > 0 {
		return resultSlice, errors.New(buffer.String())
	} else {
		return resultSlice, nil
	}
}

func shell(w io.Writer, r io.Reader, e io.Reader, interactiveMap map[string]string) (chan<- string, <-chan string, chan string) {
	inputChannel := make(chan string, 1)
	outputChannel := make(chan string, 1)
	errorChannel := make(chan string, 1024)

	waitGroup := sync.WaitGroup{}
	// Start from read
	waitGroup.Add(1)

	// Issue command
	go func() {
		for cmd := range inputChannel {
			waitGroup.Add(1)
			w.Write([]byte(cmd))
			waitGroup.Wait()
		}
	}()

	// Handle responsed error
	go func() {
		buf := make([]byte, 1024*64)
		for {
			n, err := e.Read(buf)
			if err != nil && err.Error() == "EOF" {
				close(errorChannel)
				return
			} else if err != nil {
				errorChannel <- err.Error()
				close(errorChannel)
				return
			}

			// Upon receiveing from stderr, send to error
			errorChannel <- string(buf[:n])
		}
	}()

	// Handle responsed output
	go func() {
		buf := make([]byte, 1024*64)
		length := 0
		for {
			n, err := r.Read(buf[length:])
			if err != nil && err.Error() == "EOF" {
				outputChannel <- string(buf[:length])
				close(outputChannel)
				return
			} else if err != nil {
				outputChannel <- err.Error()
				close(outputChannel)
				return
			}

			interactive := false
			currentResponse := string(buf[length:])
			for key, value := range interactiveMap {
				if strings.Contains(currentResponse, key) {
					w.Write([]byte(value))
					interactive = true
					break
				}
			}

			if interactive {
				// Ignore the response for output
			} else {
				length += n
			}

			// Keep buffing until the end of this interactive command.
			// $ is the terminal symbol where is used to tell user to enter next command.
			if length-2 > 0 && buf[length-2] == '$' {
				outputChannel <- string(buf[:length-n])
				length = 0
				waitGroup.Done()
			}
		}
	}()
	return inputChannel, outputChannel, errorChannel
}
<commit_msg>Make the buffer grow dynamically<commit_after>// Copyright 2015 CloudAwan LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package sshclient

import (
	"bytes"
	"errors"
	"github.com/cloudawan/cloudone_utility/ioutility"
	"golang.org/x/crypto/ssh"
	"io"
	"net"
	"strconv"
	"strings"
	"sync"
	"time"
)

// The function ssh.Dial doesn't have timeout mechanism for dial so this function is used
func dialWithTimeout(network, addr string, config *ssh.ClientConfig, timeout time.Duration) (*ssh.Client, error) {
	conn, err := net.DialTimeout(network, addr, timeout)
	if err != nil {
		return nil, err
	}
	c, chans, reqs, err := ssh.NewClientConn(conn, addr, config)
	if err != nil {
		return nil, err
	}
	return ssh.NewClient(c, chans, reqs), nil
}

func InteractiveSSH(dialTimeout time.Duration, sessionTimeout time.Duration, host string, port int, user string, password string,
	commandSlice []string, interactiveMap map[string]string) ([]string, error) {
	sshConfig := &ssh.ClientConfig{
		User: user,
		Auth: []ssh.AuthMethod{
			ssh.Password(password),
		},
	}

	connection, err := dialWithTimeout("tcp", host+":"+strconv.Itoa(port), sshConfig, dialTimeout)
	if err != nil {
		return nil, err
	}
	defer connection.Close()

	session, err := connection.NewSession()
	if err != nil {
		return nil, err
	}
	defer session.Close()

	modes := ssh.TerminalModes{
		ssh.ECHO:          0,     // disable echoing
		ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
		ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
	}
	if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
		return nil, err
	}
	w, err := session.StdinPipe()
	if err != nil {
		return nil, err
	}
	r, err := session.StdoutPipe()
	if err != nil {
		return nil, err
	}
	e, err := session.StderrPipe()
	if err != nil {
		return nil, err
	}

	inputChannel, outputChannel, errorChannel := shell(w, r, e, interactiveMap)
	if err := session.Shell(); err != nil {
		return nil, err
	}

	isTimeout := false
	go func() {
		// Timeout the session to prevent got stuck
		time.Sleep(sessionTimeout)
		isTimeout = true
		session.Close()
	}()

	// Ignore the ssh tty welcome page
	<-outputChannel

	resultSlice := make([]string, 0)
	for _, command := range commandSlice {
		inputChannel <- command
		result, ok := <-outputChannel
		if ok {
			resultSlice = append(resultSlice, result)
		} else {
			break
		}
	}

	// End terminal
	inputChannel <- "exit\n"
	// Close input
	close(inputChannel)

	// Wait until I/O is closed
	session.Wait()

	buffer := bytes.Buffer{}
	for {
		errorMessage, ok := <-errorChannel
		if ok {
			buffer.WriteString(errorMessage)
		} else {
			break
		}
	}

	if isTimeout {
		buffer.WriteString("Session timeout")
	}

	if buffer.Len() > 0 {
		return resultSlice, errors.New(buffer.String())
	} else {
		return resultSlice, nil
	}
}

func shell(w io.Writer, r io.Reader, e io.Reader, interactiveMap map[string]string) (chan<- string, <-chan string, chan string) {
	inputChannel := make(chan string, 1)
	outputChannel := make(chan string, 1)
	errorChannel := make(chan string, 1024)

	waitGroup := sync.WaitGroup{}
	// Start from read
	waitGroup.Add(1)

	// Issue command
	go func() {
		for cmd := range inputChannel {
			waitGroup.Add(1)
			w.Write([]byte(cmd))
			waitGroup.Wait()
		}
	}()

	// Handle responsed error
	go func() {
		for {
			text, _, err := ioutility.ReadText(e, 1024*64)
			if err == io.EOF {
				close(errorChannel)
				return
			} else if err != nil {
				errorChannel <- err.Error()
				close(errorChannel)
				return
			}

			// Upon receiveing from stderr, send to error
			errorChannel <- text
		}
	}()

	// Handle responsed output
	go func() {
		buffer := bytes.Buffer{}
		length := 0
		for {
			text, n, err := ioutility.ReadText(r, 16)
			if err == io.EOF {
				buffer.WriteString(text)
				outputChannel <- buffer.String()
				close(outputChannel)
				return
			} else if err != nil {
				outputChannel <- err.Error()
				close(outputChannel)
				return
			}

			interactive := false
			for key, value := range interactiveMap {
				if strings.Contains(text, key) {
					w.Write([]byte(value))
					interactive = true
					break
				}
			}

			if interactive {
				// Ignore the response for output
			} else {
				length += n

				_, err := buffer.WriteString(text)
				if err != nil {
					outputChannel <- err.Error()
					close(outputChannel)
					return
				}
			}

			// Keep buffing until the end of this interactive command.
			// $ is the terminal symbol where is used to tell user to enter next command.
			buf := buffer.Bytes()

			if length-2 > 0 && buf[length-2] == '$' {
				text := string(buf[:length])
				outputChannel <- text
				length = 0
				buffer.Reset()
				waitGroup.Done()
			}
		}
	}()
	return inputChannel, outputChannel, errorChannel
}
<|endoftext|>


<commit_before>package buildpacks_test

import (
	//. "github.com/davidehringer/cf-buildpack-management-plugin"

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

	"github.com/cloudfoundry/cli/plugin/fakes"
)

var _ = Describe("AddBuildpackCommand", func() {

	var cliConnection *fakes.FakeCliConnection

	BeforeEach(func() {
		cliConnection = &fakes.FakeCliConnection{}
	})

	It("it calls the add-buildpack comand", func() {

		// TODO

	})

})
<commit_msg>Implemented scenario for 'it calls the add-buildpack comand'<commit_after>package buildpacks_test

import (
	. "github.com/davidehringer/cf-buildpack-management-plugin/buildpacks"

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

	"github.com/cloudfoundry/cli/plugin/fakes"
)

var _ = Describe("AddBuildpackCommand", func() {

	var cliConnection *fakes.FakeCliConnection

	BeforeEach(func() {
		cliConnection = &fakes.FakeCliConnection{}
	})

	It("it calls the add-buildpack comand", func() {

		buildpack := Buildpack{
			Name: "example-bp",
			Position: 2,
			Enabled: true,
			Locked: false,
			Filename: "example.zip",
		}
		command := NewCliAddBuildpackCommand(cliConnection, buildpack)
		command.Execute()

		Expect(cliConnection.CliCommandCallCount()).To(Equal(1))

		args := cliConnection.CliCommandArgsForCall(0)
		Expect(args).To(Equal([]string{"create-buildpack", "example-bp", "example.zip", "2", "--enable"}))
	})

})
<|endoftext|>


<commit_before>/*
Copyright 2019 The Ceph-CSI Authors.

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

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

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

package main

import (
	"flag"
	"fmt"
	"os"
	"runtime"
	"time"

	"github.com/ceph/ceph-csi/internal/cephfs"
	"github.com/ceph/ceph-csi/internal/liveness"
	"github.com/ceph/ceph-csi/internal/rbd"
	"github.com/ceph/ceph-csi/internal/util"

	klog "k8s.io/klog/v2"
)

const (
	rbdType      = "rbd"
	cephfsType   = "cephfs"
	livenessType = "liveness"

	rbdDefaultName      = "rbd.csi.ceph.com"
	cephfsDefaultName   = "cephfs.csi.ceph.com"
	livenessDefaultName = "liveness.csi.ceph.com"

	pollTime     = 60 // seconds
	probeTimeout = 3  // seconds
)

var (
	conf util.Config
)

func init() {
	// common flags
	flag.StringVar(&conf.Vtype, "type", "", "driver type [rbd|cephfs|liveness]")
	flag.StringVar(&conf.Endpoint, "endpoint", "unix://tmp/csi.sock", "CSI endpoint")
	flag.StringVar(&conf.DriverName, "drivername", "", "name of the driver")
	flag.StringVar(&conf.NodeID, "nodeid", "", "node id")
	flag.StringVar(&conf.InstanceID, "instanceid", "", "Unique ID distinguishing this instance of Ceph CSI among other"+
		" instances, when sharing Ceph clusters across CSI instances for provisioning")
	flag.IntVar(&conf.PidLimit, "pidlimit", 0, "the PID limit to configure through cgroups")
	flag.BoolVar(&conf.IsControllerServer, "controllerserver", false, "start cephcsi controller server")
	flag.BoolVar(&conf.IsNodeServer, "nodeserver", false, "start cephcsi node server")
	flag.StringVar(&conf.DomainLabels, "domainlabels", "", "list of kubernetes node labels, that determines the topology"+
		" domain the node belongs to, separated by ','")

	// cephfs related flags
	flag.BoolVar(&conf.ForceKernelCephFS, "forcecephkernelclient", false, "enable Ceph Kernel clients on kernel < 4.17 which support quotas")

	// liveness/grpc metrics related flags
	flag.IntVar(&conf.MetricsPort, "metricsport", 8080, "TCP port for liveness/grpc metrics requests")
	flag.StringVar(&conf.MetricsPath, "metricspath", "/metrics", "path of prometheus endpoint where metrics will be available")
	flag.DurationVar(&conf.PollTime, "polltime", time.Second*pollTime, "time interval in seconds between each poll")
	flag.DurationVar(&conf.PoolTimeout, "timeout", time.Second*probeTimeout, "probe timeout in seconds")

	flag.BoolVar(&conf.EnableGRPCMetrics, "enablegrpcmetrics", false, "[DEPRECATED] enable grpc metrics")
	flag.StringVar(&conf.HistogramOption, "histogramoption", "0.5,2,6",
		"[DEPRECATED] Histogram option for grpc metrics, should be comma separated value, ex:= 0.5,2,6 where start=0.5 factor=2, count=6")

	flag.UintVar(&conf.RbdHardMaxCloneDepth, "rbdhardmaxclonedepth", 8, "Hard limit for maximum number of nested volume clones that are taken before a flatten occurs")
	flag.UintVar(&conf.RbdSoftMaxCloneDepth, "rbdsoftmaxclonedepth", 4, "Soft limit for maximum number of nested volume clones that are taken before a flatten occurs")
	flag.UintVar(&conf.MaxSnapshotsOnImage, "maxsnapshotsonimage", 450, "Maximum number of snapshots allowed on rbd image without flattening")
	flag.BoolVar(&conf.SkipForceFlatten, "skipforceflatten", false,
		"skip image flattening if kernel support mapping of rbd images which has the deep-flatten feature")

	flag.BoolVar(&conf.Version, "version", false, "Print cephcsi version information")

	klog.InitFlags(nil)
	if err := flag.Set("logtostderr", "true"); err != nil {
		klog.Exitf("failed to set logtostderr flag: %v", err)
	}
	flag.Parse()
}

func getDriverName() string {
	// was explicitly passed a driver name
	if conf.DriverName != "" {
		return conf.DriverName
	}
	// select driver name based on volume type
	switch conf.Vtype {
	case rbdType:
		return rbdDefaultName
	case cephfsType:
		return cephfsDefaultName
	case livenessType:
		return livenessDefaultName
	default:
		return ""
	}
}

func main() {
	if conf.Version {
		fmt.Println("Cephcsi Version:", util.DriverVersion)
		fmt.Println("Git Commit:", util.GitCommit)
		fmt.Println("Go Version:", runtime.Version())
		fmt.Println("Compiler:", runtime.Compiler)
		fmt.Printf("Platform: %s/%s\n", runtime.GOOS, runtime.GOARCH)
		if kv, err := util.GetKernelVersion(); err == nil {
			fmt.Println("Kernel:", kv)
		}
		os.Exit(0)
	}
	util.DefaultLog("Driver version: %s and Git version: %s", util.DriverVersion, util.GitCommit)

	if conf.Vtype == "" {
		logAndExit("driver type not specified")
	}

	dname := getDriverName()
	err := util.ValidateDriverName(dname)
	if err != nil {
		logAndExit(err.Error())
	}

	// the driver may need a higher PID limit for handling all concurrent requests
	if conf.PidLimit != 0 {
		currentLimit, pidErr := util.GetPIDLimit()
		if pidErr != nil {
			klog.Errorf("Failed to get the PID limit, can not reconfigure: %v", pidErr)
		} else {
			util.DefaultLog("Initial PID limit is set to %d", currentLimit)
			err = util.SetPIDLimit(conf.PidLimit)
			if err != nil {
				klog.Errorf("Failed to set new PID limit to %d: %v", conf.PidLimit, err)
			} else {
				s := ""
				if conf.PidLimit == -1 {
					s = " (max)"
				}
				util.DefaultLog("Reconfigured PID limit to %d%s", conf.PidLimit, s)
			}
		}
	}

	if conf.EnableGRPCMetrics || conf.Vtype == livenessType {
		// validate metrics endpoint
		conf.MetricsIP = os.Getenv("POD_IP")

		if conf.MetricsIP == "" {
			klog.Warning("missing POD_IP env var defaulting to 0.0.0.0")
			conf.MetricsIP = "0.0.0.0"
		}
		err = util.ValidateURL(&conf)
		if err != nil {
			logAndExit(err.Error())
		}
	}

	util.DefaultLog("Starting driver type: %v with name: %v", conf.Vtype, dname)
	switch conf.Vtype {
	case rbdType:
		validateCloneDepthFlag(&conf)
		validateMaxSnaphostFlag(&conf)
		driver := rbd.NewDriver()
		driver.Run(&conf)

	case cephfsType:
		driver := cephfs.NewDriver()
		driver.Run(&conf)

	case livenessType:
		liveness.Run(&conf)

	default:
		klog.Fatalln("invalid volume type", conf.Vtype) // calls exit
	}

	os.Exit(0)
}

func validateCloneDepthFlag(conf *util.Config) {
	// keeping hardlimit to 14 as max to avoid max image depth
	if conf.RbdHardMaxCloneDepth == 0 || conf.RbdHardMaxCloneDepth > 14 {
		logAndExit("rbdhardmaxclonedepth flag value should be between 1 and 14")
	}

	if conf.RbdSoftMaxCloneDepth > conf.RbdHardMaxCloneDepth {
		logAndExit("rbdsoftmaxclonedepth flag value should not be greater than rbdhardmaxclonedepth")
	}
}

func validateMaxSnaphostFlag(conf *util.Config) {
	// maximum number of snapshots on an image are 510 [1] and 16 images in
	// a parent/child chain [2],keeping snapshot limit to 500 to avoid issues.
	// [1] https://github.com/torvalds/linux/blob/master/drivers/block/rbd.c#L98
	// [2] https://github.com/torvalds/linux/blob/master/drivers/block/rbd.c#L92
	if conf.MaxSnapshotsOnImage == 0 || conf.MaxSnapshotsOnImage > 500 {
		logAndExit("maxsnapshotsonimage flag value should be between 1 and 500")
	}
}

func logAndExit(msg string) {
	klog.Errorln(msg)
	os.Exit(1)
}
<commit_msg>cleanup: no need to validate conf.Vtype twice<commit_after>/*
Copyright 2019 The Ceph-CSI Authors.

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

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

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

package main

import (
	"flag"
	"fmt"
	"os"
	"runtime"
	"time"

	"github.com/ceph/ceph-csi/internal/cephfs"
	"github.com/ceph/ceph-csi/internal/liveness"
	"github.com/ceph/ceph-csi/internal/rbd"
	"github.com/ceph/ceph-csi/internal/util"

	klog "k8s.io/klog/v2"
)

const (
	rbdType      = "rbd"
	cephfsType   = "cephfs"
	livenessType = "liveness"

	rbdDefaultName      = "rbd.csi.ceph.com"
	cephfsDefaultName   = "cephfs.csi.ceph.com"
	livenessDefaultName = "liveness.csi.ceph.com"

	pollTime     = 60 // seconds
	probeTimeout = 3  // seconds
)

var (
	conf util.Config
)

func init() {
	// common flags
	flag.StringVar(&conf.Vtype, "type", "", "driver type [rbd|cephfs|liveness]")
	flag.StringVar(&conf.Endpoint, "endpoint", "unix://tmp/csi.sock", "CSI endpoint")
	flag.StringVar(&conf.DriverName, "drivername", "", "name of the driver")
	flag.StringVar(&conf.NodeID, "nodeid", "", "node id")
	flag.StringVar(&conf.InstanceID, "instanceid", "", "Unique ID distinguishing this instance of Ceph CSI among other"+
		" instances, when sharing Ceph clusters across CSI instances for provisioning")
	flag.IntVar(&conf.PidLimit, "pidlimit", 0, "the PID limit to configure through cgroups")
	flag.BoolVar(&conf.IsControllerServer, "controllerserver", false, "start cephcsi controller server")
	flag.BoolVar(&conf.IsNodeServer, "nodeserver", false, "start cephcsi node server")
	flag.StringVar(&conf.DomainLabels, "domainlabels", "", "list of kubernetes node labels, that determines the topology"+
		" domain the node belongs to, separated by ','")

	// cephfs related flags
	flag.BoolVar(&conf.ForceKernelCephFS, "forcecephkernelclient", false, "enable Ceph Kernel clients on kernel < 4.17 which support quotas")

	// liveness/grpc metrics related flags
	flag.IntVar(&conf.MetricsPort, "metricsport", 8080, "TCP port for liveness/grpc metrics requests")
	flag.StringVar(&conf.MetricsPath, "metricspath", "/metrics", "path of prometheus endpoint where metrics will be available")
	flag.DurationVar(&conf.PollTime, "polltime", time.Second*pollTime, "time interval in seconds between each poll")
	flag.DurationVar(&conf.PoolTimeout, "timeout", time.Second*probeTimeout, "probe timeout in seconds")

	flag.BoolVar(&conf.EnableGRPCMetrics, "enablegrpcmetrics", false, "[DEPRECATED] enable grpc metrics")
	flag.StringVar(&conf.HistogramOption, "histogramoption", "0.5,2,6",
		"[DEPRECATED] Histogram option for grpc metrics, should be comma separated value, ex:= 0.5,2,6 where start=0.5 factor=2, count=6")

	flag.UintVar(&conf.RbdHardMaxCloneDepth, "rbdhardmaxclonedepth", 8, "Hard limit for maximum number of nested volume clones that are taken before a flatten occurs")
	flag.UintVar(&conf.RbdSoftMaxCloneDepth, "rbdsoftmaxclonedepth", 4, "Soft limit for maximum number of nested volume clones that are taken before a flatten occurs")
	flag.UintVar(&conf.MaxSnapshotsOnImage, "maxsnapshotsonimage", 450, "Maximum number of snapshots allowed on rbd image without flattening")
	flag.BoolVar(&conf.SkipForceFlatten, "skipforceflatten", false,
		"skip image flattening if kernel support mapping of rbd images which has the deep-flatten feature")

	flag.BoolVar(&conf.Version, "version", false, "Print cephcsi version information")

	klog.InitFlags(nil)
	if err := flag.Set("logtostderr", "true"); err != nil {
		klog.Exitf("failed to set logtostderr flag: %v", err)
	}
	flag.Parse()
}

func getDriverName() string {
	// was explicitly passed a driver name
	if conf.DriverName != "" {
		return conf.DriverName
	}
	// select driver name based on volume type
	switch conf.Vtype {
	case rbdType:
		return rbdDefaultName
	case cephfsType:
		return cephfsDefaultName
	case livenessType:
		return livenessDefaultName
	default:
		return ""
	}
}

func main() {
	if conf.Version {
		fmt.Println("Cephcsi Version:", util.DriverVersion)
		fmt.Println("Git Commit:", util.GitCommit)
		fmt.Println("Go Version:", runtime.Version())
		fmt.Println("Compiler:", runtime.Compiler)
		fmt.Printf("Platform: %s/%s\n", runtime.GOOS, runtime.GOARCH)
		if kv, err := util.GetKernelVersion(); err == nil {
			fmt.Println("Kernel:", kv)
		}
		os.Exit(0)
	}
	util.DefaultLog("Driver version: %s and Git version: %s", util.DriverVersion, util.GitCommit)

	if conf.Vtype == "" {
		logAndExit("driver type not specified")
	}

	dname := getDriverName()
	err := util.ValidateDriverName(dname)
	if err != nil {
		logAndExit(err.Error())
	}

	// the driver may need a higher PID limit for handling all concurrent requests
	if conf.PidLimit != 0 {
		currentLimit, pidErr := util.GetPIDLimit()
		if pidErr != nil {
			klog.Errorf("Failed to get the PID limit, can not reconfigure: %v", pidErr)
		} else {
			util.DefaultLog("Initial PID limit is set to %d", currentLimit)
			err = util.SetPIDLimit(conf.PidLimit)
			if err != nil {
				klog.Errorf("Failed to set new PID limit to %d: %v", conf.PidLimit, err)
			} else {
				s := ""
				if conf.PidLimit == -1 {
					s = " (max)"
				}
				util.DefaultLog("Reconfigured PID limit to %d%s", conf.PidLimit, s)
			}
		}
	}

	if conf.EnableGRPCMetrics || conf.Vtype == livenessType {
		// validate metrics endpoint
		conf.MetricsIP = os.Getenv("POD_IP")

		if conf.MetricsIP == "" {
			klog.Warning("missing POD_IP env var defaulting to 0.0.0.0")
			conf.MetricsIP = "0.0.0.0"
		}
		err = util.ValidateURL(&conf)
		if err != nil {
			logAndExit(err.Error())
		}
	}

	util.DefaultLog("Starting driver type: %v with name: %v", conf.Vtype, dname)
	switch conf.Vtype {
	case rbdType:
		validateCloneDepthFlag(&conf)
		validateMaxSnaphostFlag(&conf)
		driver := rbd.NewDriver()
		driver.Run(&conf)

	case cephfsType:
		driver := cephfs.NewDriver()
		driver.Run(&conf)

	case livenessType:
		liveness.Run(&conf)
	}

	os.Exit(0)
}

func validateCloneDepthFlag(conf *util.Config) {
	// keeping hardlimit to 14 as max to avoid max image depth
	if conf.RbdHardMaxCloneDepth == 0 || conf.RbdHardMaxCloneDepth > 14 {
		logAndExit("rbdhardmaxclonedepth flag value should be between 1 and 14")
	}

	if conf.RbdSoftMaxCloneDepth > conf.RbdHardMaxCloneDepth {
		logAndExit("rbdsoftmaxclonedepth flag value should not be greater than rbdhardmaxclonedepth")
	}
}

func validateMaxSnaphostFlag(conf *util.Config) {
	// maximum number of snapshots on an image are 510 [1] and 16 images in
	// a parent/child chain [2],keeping snapshot limit to 500 to avoid issues.
	// [1] https://github.com/torvalds/linux/blob/master/drivers/block/rbd.c#L98
	// [2] https://github.com/torvalds/linux/blob/master/drivers/block/rbd.c#L92
	if conf.MaxSnapshotsOnImage == 0 || conf.MaxSnapshotsOnImage > 500 {
		logAndExit("maxsnapshotsonimage flag value should be between 1 and 500")
	}
}

func logAndExit(msg string) {
	klog.Errorln(msg)
	os.Exit(1)
}
<|endoftext|>


<commit_before>// main.go
package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"golang.org/x/net/html/charset"
	"golang.org/x/text/transform"
	"io/ioutil"
	"os"
	"runtime"

	"github.com/kurehajime/dajarep"
)

func main() {
	var text string
	var err error
	var encode string
	var debug bool
	var interactive bool
	var default_encoding string

	if runtime.GOOS == "windows" {
		default_encoding = "sjis"
	} else {
		default_encoding = "utf-8"
	}
	flag.StringVar(&encode, "e", default_encoding, "encoding")
	flag.BoolVar(&debug, "d", false, "debug mode")
	flag.BoolVar(&interactive, "i", false, "interactive mode")

	flag.Parse()

	if interactive == true {
		fmt.Print("> ")
	} else if len(flag.Args()) == 0 {
		text, err = readPipe()
	} else if flag.Arg(0) == "-" {
		text, err = readStdin()
	} else {
		text, err = readFileByArg(flag.Arg(0))
	}
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	if interactive == false {
		text, err := transEnc(text, encode)
		if err != nil {
			fmt.Fprintln(os.Stderr, err.Error())
			os.Exit(1)
		}
		s, d := dajarep.Dajarep(text)
		for i := 0; i < len(s); i++ {
			if !debug {
				fmt.Println(s[i])
			} else {
				fmt.Println(s[i] + "[" + d[i] + "]")
			}
		}
	} else {
		//interactive mode
		s := bufio.NewScanner(os.Stdin)
		for s.Scan() {
			if s.Err() != nil {
				fmt.Fprintln(os.Stderr, s.Err())
				os.Exit(1)
			}
			if s.Text() == "" {
				break
			}
			text, err := transEnc(s.Text(), encode)
			if err != nil {
				fmt.Fprintln(os.Stderr, err.Error())
				os.Exit(1)
			}
			_, d := dajarep.Dajarep(text)
			if len(d) > 0 {
				for i := 0; i < len(d); i++ {
					fmt.Println("-> " + d[i])
				}
			} else {
				fmt.Println("")
			}
			fmt.Print("> ")
		}
	}
}

func readPipe() (string, error) {
	stats, _ := os.Stdin.Stat()
	if stats != nil && (stats.Mode()&os.ModeCharDevice) == 0 {
		bytes, err := ioutil.ReadAll(os.Stdin)
		if err != nil {
			return "", err
		}
		return string(bytes), nil
	} else {
		return "", nil
	}
}
func readStdin() (string, error) {
	var text string
	s := bufio.NewScanner(os.Stdin)
	for s.Scan() {
		if s.Text() == "" {
			break
		}
		text += s.Text() + "\n"
	}
	if s.Err() != nil {
		return "", s.Err()
	}
	return text, nil
}

func readFileByArg(path string) (string, error) {
	content, err := ioutil.ReadFile(path)
	if err != nil {
		return "", err
	}
	return string(content), nil
}

//「Golangで文字コード判定」qiita.com/nobuhito/items/ff782f64e32f7ed95e43
func transEnc(text string, encode string) (string, error) {
	body := []byte(text)
	var f []byte

	encodings := []string{"sjis", "utf-8"}
	if encode != "" {
		encodings = append([]string{encode}, encodings...)
	}
	for _, enc := range encodings {
		if enc != "" {
			ee, _ := charset.Lookup(enc)
			if ee == nil {
				continue
			}
			var buf bytes.Buffer
			ic := transform.NewWriter(&buf, ee.NewDecoder())
			_, err := ic.Write(body)
			if err != nil {
				continue
			}
			err = ic.Close()
			if err != nil {
				continue
			}
			f = buf.Bytes()
			break
		}
	}
	return string(f), nil
}
<commit_msg>temporary<commit_after>// main.go
package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"golang.org/x/net/html/charset"
	"golang.org/x/text/transform"
	"io/ioutil"
	"os"
	"runtime"

	"github.com/ikawaha/dajarep" // temporary
)

func main() {
	var text string
	var err error
	var encode string
	var debug bool
	var interactive bool
	var default_encoding string

	if runtime.GOOS == "windows" {
		default_encoding = "sjis"
	} else {
		default_encoding = "utf-8"
	}
	flag.StringVar(&encode, "e", default_encoding, "encoding")
	flag.BoolVar(&debug, "d", false, "debug mode")
	flag.BoolVar(&interactive, "i", false, "interactive mode")

	flag.Parse()

	if interactive == true {
		fmt.Print("> ")
	} else if len(flag.Args()) == 0 {
		text, err = readPipe()
	} else if flag.Arg(0) == "-" {
		text, err = readStdin()
	} else {
		text, err = readFileByArg(flag.Arg(0))
	}
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	if interactive == false {
		text, err := transEnc(text, encode)
		if err != nil {
			fmt.Fprintln(os.Stderr, err.Error())
			os.Exit(1)
		}
		s, d := dajarep.Dajarep(text)
		for i := 0; i < len(s); i++ {
			if !debug {
				fmt.Println(s[i])
			} else {
				fmt.Println(s[i] + "[" + d[i] + "]")
			}
		}
	} else {
		//interactive mode
		s := bufio.NewScanner(os.Stdin)
		for s.Scan() {
			if s.Err() != nil {
				fmt.Fprintln(os.Stderr, s.Err())
				os.Exit(1)
			}
			if s.Text() == "" {
				break
			}
			text, err := transEnc(s.Text(), encode)
			if err != nil {
				fmt.Fprintln(os.Stderr, err.Error())
				os.Exit(1)
			}
			_, d := dajarep.Dajarep(text)
			if len(d) > 0 {
				for i := 0; i < len(d); i++ {
					fmt.Println("-> " + d[i])
				}
			} else {
				fmt.Println("")
			}
			fmt.Print("> ")
		}
	}
}

func readPipe() (string, error) {
	stats, _ := os.Stdin.Stat()
	if stats != nil && (stats.Mode()&os.ModeCharDevice) == 0 {
		bytes, err := ioutil.ReadAll(os.Stdin)
		if err != nil {
			return "", err
		}
		return string(bytes), nil
	} else {
		return "", nil
	}
}
func readStdin() (string, error) {
	var text string
	s := bufio.NewScanner(os.Stdin)
	for s.Scan() {
		if s.Text() == "" {
			break
		}
		text += s.Text() + "\n"
	}
	if s.Err() != nil {
		return "", s.Err()
	}
	return text, nil
}

func readFileByArg(path string) (string, error) {
	content, err := ioutil.ReadFile(path)
	if err != nil {
		return "", err
	}
	return string(content), nil
}

//「Golangで文字コード判定」qiita.com/nobuhito/items/ff782f64e32f7ed95e43
func transEnc(text string, encode string) (string, error) {
	body := []byte(text)
	var f []byte

	encodings := []string{"sjis", "utf-8"}
	if encode != "" {
		encodings = append([]string{encode}, encodings...)
	}
	for _, enc := range encodings {
		if enc != "" {
			ee, _ := charset.Lookup(enc)
			if ee == nil {
				continue
			}
			var buf bytes.Buffer
			ic := transform.NewWriter(&buf, ee.NewDecoder())
			_, err := ic.Write(body)
			if err != nil {
				continue
			}
			err = ic.Close()
			if err != nil {
				continue
			}
			f = buf.Bytes()
			break
		}
	}
	return string(f), nil
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Google Inc. All rights reserved.

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

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

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

package main

import (
	"flag"
	"io/ioutil"
	"os"
	"runtime"
	"strconv"
	"time"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/clientauth"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	"github.com/golang/glog"
)

var (
	authConfig = flag.String("auth_config", os.Getenv("HOME")+"/.kubernetes_auth", "Path to the auth info file.")
	host       = flag.String("host", "", "The host to connect to")
)

func waitForPodRunning(c *client.Client, id string) {
	for {
		time.Sleep(5 * time.Second)
		pod, err := c.Pods(api.NamespaceDefault).Get(id)
		if err != nil {
			glog.Warningf("Get pod failed: %v", err)
			continue
		}
		if pod.Status.Phase == api.PodRunning {
			break
		}
		glog.Infof("Waiting for pod status to be running (%s)", pod.Status.Phase)
	}
}

func loadObjectOrDie(filePath string) interface{} {
	data, err := ioutil.ReadFile(filePath)
	if err != nil {
		glog.Fatalf("Failed to read pod: %v", err)
	}
	obj, err := latest.Codec.Decode(data)
	if err != nil {
		glog.Fatalf("Failed to decode pod: %v", err)
	}
	return obj
}

func loadPodOrDie(filePath string) *api.Pod {
	obj := loadObjectOrDie(filePath)
	pod, ok := obj.(*api.Pod)
	if !ok {
		glog.Fatalf("Failed to load pod: %v", obj)
	}
	return pod
}

func loadClientOrDie() *client.Client {
	config := client.Config{
		Host: *host,
	}
	auth, err := clientauth.LoadFromFile(*authConfig)
	if err != nil {
		glog.Fatalf("Error loading auth: %v", err)
	}
	config, err = auth.MergeWithConfig(config)
	if err != nil {
		glog.Fatalf("Error creating client")
	}
	c, err := client.New(&config)
	if err != nil {
		glog.Fatalf("Error creating client")
	}
	return c
}

func TestKubernetesROService(c *client.Client) bool {
	svc := api.ServiceList{}
	err := c.Get().
		Namespace("default").
		AbsPath("/api/v1beta1/proxy/services/kubernetes-ro/api/v1beta1/services").
		Do().
		Into(&svc)
	if err != nil {
		glog.Errorf("unexpected error listing services using ro service: %v", err)
		return false
	}
	var foundRW, foundRO bool
	for i := range svc.Items {
		if svc.Items[i].Name == "kubernetes" {
			foundRW = true
		}
		if svc.Items[i].Name == "kubernetes-ro" {
			foundRO = true
		}
	}
	if !foundRW {
		glog.Error("no RW service found")
	}
	if !foundRO {
		glog.Error("no RO service found")
	}
	if !foundRW || !foundRO {
		return false
	}
	return true
}

func TestPodUpdate(c *client.Client) bool {
	podClient := c.Pods(api.NamespaceDefault)

	pod := loadPodOrDie("./api/examples/pod.json")
	value := strconv.Itoa(time.Now().Nanosecond())
	pod.Labels["time"] = value

	_, err := podClient.Create(pod)
	if err != nil {
		glog.Errorf("Failed to create pod: %v", err)
		return false
	}
	defer podClient.Delete(pod.Name)
	waitForPodRunning(c, pod.Name)
	pods, err := podClient.List(labels.SelectorFromSet(labels.Set(map[string]string{"time": value})))
	if len(pods.Items) != 1 {
		glog.Errorf("Failed to find the correct pod")
		return false
	}

	podOut, err := podClient.Get(pod.Name)
	if err != nil {
		glog.Errorf("Failed to get pod: %v", err)
		return false
	}
	value = "time" + value
	pod.Labels["time"] = value
	pod.ResourceVersion = podOut.ResourceVersion
	pod.UID = podOut.UID
	pod, err = podClient.Update(pod)
	if err != nil {
		glog.Errorf("Failed to update pod: %v", err)
		return false
	}
	waitForPodRunning(c, pod.Name)
	pods, err = podClient.List(labels.SelectorFromSet(labels.Set(map[string]string{"time": value})))
	if len(pods.Items) != 1 {
		glog.Errorf("Failed to find the correct pod after update.")
		return false
	}
	glog.Infof("pod update OK")
	return true
}

// TestImportantURLs validates that URLs that people depend on haven't moved.
// ***IMPORTANT*** Do *not* fix this test just by changing the path.  If you moved a URL
// you can break upstream dependencies.
func TestImportantURLs(c *client.Client) bool {
	tests := []struct {
		path string
	}{}
	ok := true
	for _, test := range tests {
		glog.Infof("testing: %s", test.path)
		data, err := c.RESTClient.Get().
			AbsPath(test.path).
			Do().
			Raw()
	}
	if err != nil {
		glog.Errorf("Failed: %v\nBody: %s", err, string(data))
		ok := false
	}
	return ok
}

// TestKubeletSendsEvent checks that kubelets and scheduler send events about pods scheduling and running.
func TestKubeletSendsEvent(c *client.Client) bool {
	provider := os.Getenv("KUBERNETES_PROVIDER")
	if provider != "gce" {
		glog.Infof("skipping TestKubeletSendsEvent on cloud provider %s", provider)
		return true
	}
	if provider == "" {
		glog.Info("KUBERNETES_PROVIDER is unset assuming \"gce\"")
	}

	podClient := c.Pods(api.NamespaceDefault)

	pod := loadPodOrDie("./cmd/e2e/pod.json")
	value := strconv.Itoa(time.Now().Nanosecond())
	pod.Labels["time"] = value

	_, err := podClient.Create(pod)
	if err != nil {
		glog.Errorf("Failed to create pod: %v", err)
		return false
	}
	defer podClient.Delete(pod.Name)
	waitForPodRunning(c, pod.Name)
	pods, err := podClient.List(labels.SelectorFromSet(labels.Set(map[string]string{"time": value})))
	if len(pods.Items) != 1 {
		glog.Errorf("Failed to find the correct pod")
		return false
	}

	_, err = podClient.Get(pod.Name)
	if err != nil {
		glog.Errorf("Failed to get pod: %v", err)
		return false
	}

	// Check for scheduler event about the pod.
	events, err := c.Events(api.NamespaceDefault).List(
		labels.Everything(),
		labels.Set{
			"involvedObject.name":      pod.Name,
			"involvedObject.kind":      "Pod",
			"involvedObject.namespace": api.NamespaceDefault,
			"source":                   "scheduler",
			"time":                     value,
		}.AsSelector(),
	)
	if err != nil {
		glog.Error("Error while listing events:", err)
		return false
	}
	if len(events.Items) == 0 {
		glog.Error("Didn't see any scheduler events even though pod was running.")
		return false
	}
	glog.Info("Saw scheduler event for our pod.")

	// Check for kubelet event about the pod.
	events, err = c.Events(api.NamespaceDefault).List(
		labels.Everything(),
		labels.Set{
			"involvedObject.name":      pod.Name,
			"involvedObject.kind":      "BoundPod",
			"involvedObject.namespace": api.NamespaceDefault,
			"source":                   "kubelet",
		}.AsSelector(),
	)
	if err != nil {
		glog.Error("Error while listing events:", err)
		return false
	}
	if len(events.Items) == 0 {
		glog.Error("Didn't see any kubelet events even though pod was running.")
		return false
	}
	glog.Info("Saw kubelet event for our pod.")
	return true
}

func main() {
	flag.Parse()
	runtime.GOMAXPROCS(runtime.NumCPU())
	util.ReallyCrash = true
	util.InitLogs()
	defer util.FlushLogs()

	go func() {
		defer util.FlushLogs()
		time.Sleep(3 * time.Minute)
		glog.Fatalf("This test has timed out.")
	}()

	c := loadClientOrDie()

	tests := []func(c *client.Client) bool{
		TestKubernetesROService,
		TestKubeletSendsEvent,
		// TODO(brendandburns): fix this test and re-add it: TestPodUpdate,
	}

	passed := true
	for _, test := range tests {
		testPassed := test(c)
		if !testPassed {
			passed = false
		}
		// TODO: clean up objects created during a test after the test, so cases
		// are independent.
	}
	if !passed {
		glog.Fatalf("Tests failed")
	}
}
<commit_msg>Add a test for important URLs on the apiserver.<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.

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

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

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

package main

import (
	"flag"
	"io/ioutil"
	"os"
	"runtime"
	"strconv"
	"time"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/clientauth"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	"github.com/golang/glog"
)

var (
	authConfig = flag.String("auth_config", os.Getenv("HOME")+"/.kubernetes_auth", "Path to the auth info file.")
	host       = flag.String("host", "", "The host to connect to")
)

func waitForPodRunning(c *client.Client, id string) {
	for {
		time.Sleep(5 * time.Second)
		pod, err := c.Pods(api.NamespaceDefault).Get(id)
		if err != nil {
			glog.Warningf("Get pod failed: %v", err)
			continue
		}
		if pod.Status.Phase == api.PodRunning {
			break
		}
		glog.Infof("Waiting for pod status to be running (%s)", pod.Status.Phase)
	}
}

func loadObjectOrDie(filePath string) interface{} {
	data, err := ioutil.ReadFile(filePath)
	if err != nil {
		glog.Fatalf("Failed to read pod: %v", err)
	}
	obj, err := latest.Codec.Decode(data)
	if err != nil {
		glog.Fatalf("Failed to decode pod: %v", err)
	}
	return obj
}

func loadPodOrDie(filePath string) *api.Pod {
	obj := loadObjectOrDie(filePath)
	pod, ok := obj.(*api.Pod)
	if !ok {
		glog.Fatalf("Failed to load pod: %v", obj)
	}
	return pod
}

func loadClientOrDie() *client.Client {
	config := client.Config{
		Host: *host,
	}
	auth, err := clientauth.LoadFromFile(*authConfig)
	if err != nil {
		glog.Fatalf("Error loading auth: %v", err)
	}
	config, err = auth.MergeWithConfig(config)
	if err != nil {
		glog.Fatalf("Error creating client")
	}
	c, err := client.New(&config)
	if err != nil {
		glog.Fatalf("Error creating client")
	}
	return c
}

func TestKubernetesROService(c *client.Client) bool {
	svc := api.ServiceList{}
	err := c.Get().
		Namespace("default").
		AbsPath("/api/v1beta1/proxy/services/kubernetes-ro/api/v1beta1/services").
		Do().
		Into(&svc)
	if err != nil {
		glog.Errorf("unexpected error listing services using ro service: %v", err)
		return false
	}
	var foundRW, foundRO bool
	for i := range svc.Items {
		if svc.Items[i].Name == "kubernetes" {
			foundRW = true
		}
		if svc.Items[i].Name == "kubernetes-ro" {
			foundRO = true
		}
	}
	if !foundRW {
		glog.Error("no RW service found")
	}
	if !foundRO {
		glog.Error("no RO service found")
	}
	if !foundRW || !foundRO {
		return false
	}
	return true
}

func TestPodUpdate(c *client.Client) bool {
	podClient := c.Pods(api.NamespaceDefault)

	pod := loadPodOrDie("./api/examples/pod.json")
	value := strconv.Itoa(time.Now().Nanosecond())
	pod.Labels["time"] = value

	_, err := podClient.Create(pod)
	if err != nil {
		glog.Errorf("Failed to create pod: %v", err)
		return false
	}
	defer podClient.Delete(pod.Name)
	waitForPodRunning(c, pod.Name)
	pods, err := podClient.List(labels.SelectorFromSet(labels.Set(map[string]string{"time": value})))
	if len(pods.Items) != 1 {
		glog.Errorf("Failed to find the correct pod")
		return false
	}

	podOut, err := podClient.Get(pod.Name)
	if err != nil {
		glog.Errorf("Failed to get pod: %v", err)
		return false
	}
	value = "time" + value
	pod.Labels["time"] = value
	pod.ResourceVersion = podOut.ResourceVersion
	pod.UID = podOut.UID
	pod, err = podClient.Update(pod)
	if err != nil {
		glog.Errorf("Failed to update pod: %v", err)
		return false
	}
	waitForPodRunning(c, pod.Name)
	pods, err = podClient.List(labels.SelectorFromSet(labels.Set(map[string]string{"time": value})))
	if len(pods.Items) != 1 {
		glog.Errorf("Failed to find the correct pod after update.")
		return false
	}
	glog.Infof("pod update OK")
	return true
}

// TestImportantURLs validates that URLs that people depend on haven't moved.
// ***IMPORTANT*** Do *not* fix this test just by changing the path.  If you moved a URL
// you can break upstream dependencies.
func TestImportantURLs(c *client.Client) bool {
	tests := []struct {
		path string
	}{
		{path: "/validate"},
		{path: "/healthz"},
		// TODO: test proxy links here
	}
	ok := true
	for _, test := range tests {
		glog.Infof("testing: %s", test.path)
		data, err := c.RESTClient.Get().
			AbsPath(test.path).
			Do().
			Raw()
		if err != nil {
			glog.Errorf("Failed: %v\nBody: %s", err, string(data))
			ok = false
		}
	}
	return ok
}

// TestKubeletSendsEvent checks that kubelets and scheduler send events about pods scheduling and running.
func TestKubeletSendsEvent(c *client.Client) bool {
	provider := os.Getenv("KUBERNETES_PROVIDER")
	if provider != "gce" {
		glog.Infof("skipping TestKubeletSendsEvent on cloud provider %s", provider)
		return true
	}
	if provider == "" {
		glog.Info("KUBERNETES_PROVIDER is unset assuming \"gce\"")
	}

	podClient := c.Pods(api.NamespaceDefault)

	pod := loadPodOrDie("./cmd/e2e/pod.json")
	value := strconv.Itoa(time.Now().Nanosecond())
	pod.Labels["time"] = value

	_, err := podClient.Create(pod)
	if err != nil {
		glog.Errorf("Failed to create pod: %v", err)
		return false
	}
	defer podClient.Delete(pod.Name)
	waitForPodRunning(c, pod.Name)
	pods, err := podClient.List(labels.SelectorFromSet(labels.Set(map[string]string{"time": value})))
	if len(pods.Items) != 1 {
		glog.Errorf("Failed to find the correct pod")
		return false
	}

	_, err = podClient.Get(pod.Name)
	if err != nil {
		glog.Errorf("Failed to get pod: %v", err)
		return false
	}

	// Check for scheduler event about the pod.
	events, err := c.Events(api.NamespaceDefault).List(
		labels.Everything(),
		labels.Set{
			"involvedObject.name":      pod.Name,
			"involvedObject.kind":      "Pod",
			"involvedObject.namespace": api.NamespaceDefault,
			"source":                   "scheduler",
			"time":                     value,
		}.AsSelector(),
	)
	if err != nil {
		glog.Error("Error while listing events:", err)
		return false
	}
	if len(events.Items) == 0 {
		glog.Error("Didn't see any scheduler events even though pod was running.")
		return false
	}
	glog.Info("Saw scheduler event for our pod.")

	// Check for kubelet event about the pod.
	events, err = c.Events(api.NamespaceDefault).List(
		labels.Everything(),
		labels.Set{
			"involvedObject.name":      pod.Name,
			"involvedObject.kind":      "BoundPod",
			"involvedObject.namespace": api.NamespaceDefault,
			"source":                   "kubelet",
		}.AsSelector(),
	)
	if err != nil {
		glog.Error("Error while listing events:", err)
		return false
	}
	if len(events.Items) == 0 {
		glog.Error("Didn't see any kubelet events even though pod was running.")
		return false
	}
	glog.Info("Saw kubelet event for our pod.")
	return true
}

func main() {
	flag.Parse()
	runtime.GOMAXPROCS(runtime.NumCPU())
	util.ReallyCrash = true
	util.InitLogs()
	defer util.FlushLogs()

	go func() {
		defer util.FlushLogs()
		time.Sleep(3 * time.Minute)
		glog.Fatalf("This test has timed out.")
	}()

	c := loadClientOrDie()

	tests := []func(c *client.Client) bool{
		TestKubernetesROService,
		TestKubeletSendsEvent,
		TestImportantURLs,
		// TODO(brendandburns): fix this test and re-add it: TestPodUpdate,
	}

	passed := true
	for _, test := range tests {
		testPassed := test(c)
		if !testPassed {
			passed = false
		}
		// TODO: clean up objects created during a test after the test, so cases
		// are independent.
	}
	if !passed {
		glog.Fatalf("Tests failed")
	}
}
<|endoftext|>


<commit_before>//go:build ignore
// +build ignore

/*
 * Copyright (c) 2021 The GoPlus Authors (goplus.org). 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 main

import (
	"bytes"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

func getcwd() string {
	path, _ := os.Getwd()
	return path
}

func checkPathExist(path string) bool {
	_, err := os.Stat(path)
	return !os.IsNotExist(err)
}

var gopRoot = getcwd()
var initCommandExecuteEnv = os.Environ()
var commandExecuteEnv = initCommandExecuteEnv

func execCommand(command string, arg ...string) (string, string, error) {
	var stdout, stderr bytes.Buffer
	cmd := exec.Command(command, arg...)
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	cmd.Env = commandExecuteEnv
	err := cmd.Run()
	return stdout.String(), stderr.String(), err
}

func getRevCommit(tag string) string {
	commit, stderr, err := execCommand("git", "rev-parse", "--verify", tag)
	if err != nil || stderr != "" {
		return ""
	}
	return strings.TrimRight(commit, "\n")
}

func getGitInfo() (string, bool) {
	gitDir := filepath.Join(gopRoot, ".git")
	if checkPathExist(gitDir) {
		return getRevCommit("HEAD"), true
	}
	return "", false
}

func getBuildDateTime() string {
	now := time.Now()
	return now.Format("2006-01-02_15-04-05")
}

func getBuildVer() string {
	tagRet, tagErr, err := execCommand("git", "describe", "--tags")
	if err != nil || tagErr != "" {
		return ""
	}
	return strings.TrimRight(tagRet, "\n")
}

/*
func findTag(commit string) string {
	tagRet, tagErr, err := execCommand("git", "tag")
	if err != nil || tagErr != "" {
		return ""
	}
	var prefix = "v" + env.MainVersion + "."
	for _, tag := range strings.Split(tagRet, "\n") {
		if strings.HasPrefix(tag, prefix) {
			if getRevCommit(tag) == commit {
				return tag
			}
		}
	}
	return ""
}
*/

func getGopBuildFlags() string {
	defaultGopRoot := gopRoot
	if gopRootFinal := os.Getenv("GOPROOT_FINAL"); gopRootFinal != "" {
		defaultGopRoot = gopRootFinal
	}
	buildFlags := fmt.Sprintf("-X \"github.com/goplus/gop/env.defaultGopRoot=%s\"", defaultGopRoot)
	buildFlags += fmt.Sprintf(" -X \"github.com/goplus/gop/env.buildDate=%s\"", getBuildDateTime())
	if commit, ok := getGitInfo(); ok {
		buildFlags += fmt.Sprintf(" -X github.com/goplus/gop/env.buildCommit=%s", commit)
		if buildVer := getBuildVer(); buildVer != "" {
			buildFlags += fmt.Sprintf(" -X github.com/goplus/gop/env.buildVersion=%s", buildVer)
		}
	}
	return buildFlags
}

/*
	func detectGoBinPath() string {
		goBin, ok := os.LookupEnv("GOBIN")
		if ok {
			return goBin
		}

		goPath, ok := os.LookupEnv("GOPATH")
		if ok {
			return filepath.Join(goPath, "bin")
		}

		homeDir, _ := os.UserHomeDir()
		return filepath.Join(homeDir, "go", "bin")
	}
*/

func detectGopBinPath() string {
	return filepath.Join(gopRoot, "bin")
}

func buildGoplusTools(useGoProxy bool) {
	commandsDir := filepath.Join(gopRoot, "cmd")
	if !checkPathExist(commandsDir) {
		println("Error: This script should be run at the root directory of gop repository.")
		os.Exit(1)
	}

	buildFlags := getGopBuildFlags()

	if useGoProxy {
		println("Info: we will use goproxy.cn as a Go proxy to accelerate installing process.")
		commandExecuteEnv = append(commandExecuteEnv,
			"GOPROXY=https://goproxy.cn,direct",
		)
	}

	// Install Go+ binary files under current ./bin directory.
	gopBinPath := detectGopBinPath()
	clean()
	os.Mkdir(gopBinPath, 0755)

	println("Installing Go+ tools...")
	os.Chdir(commandsDir)
	buildOutput, buildErr, err := execCommand("go", "build", "-o", gopBinPath, "-v", "-ldflags", buildFlags, "./...")
	println(buildErr)
	if err != nil {
		println(err.Error())
		os.Exit(1)
	}
	println(buildOutput)

	println("Go+ tools installed successfully!")
	showHelpPostInstall()
}

func showHelpPostInstall() {
	println("Next:")
	println("We just installed Go+ into the directory: ", detectGopBinPath())
	message := `
To setup a better Go+ development environment,
we recommend you add the above install directory into your PATH environment variable.
	`
	println(message)
}

func runTestcases() {
	println("Start running testcases.")
	os.Chdir(gopRoot)

	coverage := "-coverprofile=coverage.txt"
	gopCommand := filepath.Join(detectGopBinPath(), "gop")
	testOutput, testErr, err := execCommand(gopCommand, "test", coverage, "-covermode=atomic", "./...")
	println(testOutput)
	println(testErr)
	if err != nil {
		println(err.Error())
	}

	println("End running testcases.")
}

func clean() {
	gopBinPath := detectGopBinPath()
	if checkPathExist(gopBinPath) {
		if err := os.RemoveAll(gopBinPath); err != nil {
			println(err.Error())
		}
	}
}

func uninstall() {
	println("Uninstalling Go+ and related tools.")
	clean()
	println("Go+ and related tools uninstalled successfully.")
}

func isInChina() bool {
	const prefix = "LANG=\""
	out, errMsg, err := execCommand("locale")
	if err != nil || errMsg != "" {
		return false
	}
	if strings.HasPrefix(out, prefix) {
		out = out[len(prefix):]
		return strings.HasPrefix(out, "zh_CN") || strings.HasPrefix(out, "zh_HK")
	}
	return false
}

func main() {
	isInstall := flag.Bool("install", false, "Install Go+")
	isTest := flag.Bool("test", false, "Run testcases")
	isUninstall := flag.Bool("uninstall", false, "Uninstall Go+")
	isGoProxy := flag.Bool("proxy", false, "Set GOPROXY for people in China")
	isAutoProxy := flag.Bool("autoproxy", false, "Check to set GOPROXY automatically")

	flag.Parse()

	useGoProxy := *isGoProxy
	if !useGoProxy && *isAutoProxy {
		useGoProxy = isInChina()
	}
	flagActionMap := map[*bool]func(){
		isInstall:   func() { buildGoplusTools(useGoProxy) },
		isUninstall: uninstall,
		isTest:      runTestcases,
	}

	for flag, action := range flagActionMap {
		if *flag {
			action()
			return
		}
	}

	println("Usage:\n")
	flag.PrintDefaults()
}
<commit_msg>Fix: make sure cmd/install.go run in the root dir of gop repo<commit_after>//go:build ignore
// +build ignore

/*
 * Copyright (c) 2021 The GoPlus Authors (goplus.org). 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 main

import (
	"bytes"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

func checkPathExist(path string, isDir bool) bool {
	stat, err := os.Stat(path)
	isExists := !os.IsNotExist(err)
	if isDir {
		return isExists && stat.IsDir()
	}
	return isExists
}

// Path returns single path to check
type Path struct {
	path  string
	isDir bool
}

func (p *Path) checkExists(rootDir string) bool {
	absPath := filepath.Join(rootDir, p.path)
	return checkPathExist(absPath, p.isDir)
}

func getGopRoot() string {
	pwd, _ := os.Getwd()

	pathsToCheck := []Path{
		Path{path: "cmd", isDir: true},
		Path{path: "builtin", isDir: true},
		Path{path: "go.mod", isDir: false},
		Path{path: "go.sum", isDir: false},
	}

	for _, path := range pathsToCheck {
		if !path.checkExists(pwd) {
			println("Error: This script should be run at the root directory of gop repository.")
			os.Exit(1)
		}
	}

	return pwd
}

var gopRoot = getGopRoot()
var initCommandExecuteEnv = os.Environ()
var commandExecuteEnv = initCommandExecuteEnv

func execCommand(command string, arg ...string) (string, string, error) {
	var stdout, stderr bytes.Buffer
	cmd := exec.Command(command, arg...)
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	cmd.Env = commandExecuteEnv
	err := cmd.Run()
	return stdout.String(), stderr.String(), err
}

func getRevCommit(tag string) string {
	commit, stderr, err := execCommand("git", "rev-parse", "--verify", tag)
	if err != nil || stderr != "" {
		return ""
	}
	return strings.TrimRight(commit, "\n")
}

func getGitInfo() (string, bool) {
	gitDir := filepath.Join(gopRoot, ".git")
	if checkPathExist(gitDir, true) {
		return getRevCommit("HEAD"), true
	}
	return "", false
}

func getBuildDateTime() string {
	now := time.Now()
	return now.Format("2006-01-02_15-04-05")
}

func getBuildVer() string {
	tagRet, tagErr, err := execCommand("git", "describe", "--tags")
	if err != nil || tagErr != "" {
		return ""
	}
	return strings.TrimRight(tagRet, "\n")
}

func getGopBuildFlags() string {
	defaultGopRoot := gopRoot
	if gopRootFinal := os.Getenv("GOPROOT_FINAL"); gopRootFinal != "" {
		defaultGopRoot = gopRootFinal
	}
	buildFlags := fmt.Sprintf("-X \"github.com/goplus/gop/env.defaultGopRoot=%s\"", defaultGopRoot)
	buildFlags += fmt.Sprintf(" -X \"github.com/goplus/gop/env.buildDate=%s\"", getBuildDateTime())
	if commit, ok := getGitInfo(); ok {
		buildFlags += fmt.Sprintf(" -X github.com/goplus/gop/env.buildCommit=%s", commit)
		if buildVer := getBuildVer(); buildVer != "" {
			buildFlags += fmt.Sprintf(" -X github.com/goplus/gop/env.buildVersion=%s", buildVer)
		}
	}
	return buildFlags
}

func detectGopBinPath() string {
	return filepath.Join(gopRoot, "bin")
}

func buildGoplusTools(useGoProxy bool) {
	commandsDir := filepath.Join(gopRoot, "cmd")
	buildFlags := getGopBuildFlags()

	if useGoProxy {
		println("Info: we will use goproxy.cn as a Go proxy to accelerate installing process.")
		commandExecuteEnv = append(commandExecuteEnv,
			"GOPROXY=https://goproxy.cn,direct",
		)
	}

	// Install Go+ binary files under current ./bin directory.
	gopBinPath := detectGopBinPath()
	clean()
	if err := os.Mkdir(gopBinPath, 0755); err != nil {
		println(err.Error())
		println("Error: Go+ can't create ./bin directory to put build assets.")
		os.Exit(1)
	}

	println("Installing Go+ tools...")
	os.Chdir(commandsDir)
	buildOutput, buildErr, err := execCommand("go", "build", "-o", gopBinPath, "-v", "-ldflags", buildFlags, "./...")
	println(buildErr)
	if err != nil {
		println(err.Error())
		os.Exit(1)
	}
	println(buildOutput)

	println("Go+ tools installed successfully!")
	showHelpPostInstall()
}

func showHelpPostInstall() {
	println("Next:")
	println("We just installed Go+ into the directory: ", detectGopBinPath())
	message := `
To setup a better Go+ development environment,
we recommend you add the above install directory into your PATH environment variable.
	`
	println(message)
}

func runTestcases() {
	println("Start running testcases.")
	os.Chdir(gopRoot)

	coverage := "-coverprofile=coverage.txt"
	gopCommand := filepath.Join(detectGopBinPath(), "gop")
	testOutput, testErr, err := execCommand(gopCommand, "test", coverage, "-covermode=atomic", "./...")
	println(testOutput)
	println(testErr)
	if err != nil {
		println(err.Error())
	}

	println("End running testcases.")
}

func clean() {
	gopBinPath := detectGopBinPath()
	if checkPathExist(gopBinPath, true) {
		if err := os.RemoveAll(gopBinPath); err != nil {
			println(err.Error())
		}
	}
}

func uninstall() {
	println("Uninstalling Go+ and related tools.")
	clean()
	println("Go+ and related tools uninstalled successfully.")
}

func isInChina() bool {
	const prefix = "LANG=\""
	out, errMsg, err := execCommand("locale")
	if err != nil || errMsg != "" {
		return false
	}
	if strings.HasPrefix(out, prefix) {
		out = out[len(prefix):]
		return strings.HasPrefix(out, "zh_CN") || strings.HasPrefix(out, "zh_HK")
	}
	return false
}

func main() {
	isInstall := flag.Bool("install", false, "Install Go+")
	isTest := flag.Bool("test", false, "Run testcases")
	isUninstall := flag.Bool("uninstall", false, "Uninstall Go+")
	isGoProxy := flag.Bool("proxy", false, "Set GOPROXY for people in China")
	isAutoProxy := flag.Bool("autoproxy", false, "Check to set GOPROXY automatically")

	flag.Parse()

	useGoProxy := *isGoProxy
	if !useGoProxy && *isAutoProxy {
		useGoProxy = isInChina()
	}
	flagActionMap := map[*bool]func(){
		isInstall:   func() { buildGoplusTools(useGoProxy) },
		isUninstall: uninstall,
		isTest:      runTestcases,
	}

	for flag, action := range flagActionMap {
		if *flag {
			action()
			return
		}
	}

	println("Usage:\n")
	flag.PrintDefaults()
}
<|endoftext|>