<commit_before>package kubernetes

import (
	"fmt"
	"io/ioutil"
	"path/filepath"
	"testing"

	"github.com/docker/cli/cli/compose/loader"
	composetypes "github.com/docker/cli/cli/compose/types"
	"github.com/docker/compose-on-kubernetes/api/compose/v1alpha3"
	"github.com/docker/compose-on-kubernetes/api/compose/v1beta1"
	"github.com/docker/compose-on-kubernetes/api/compose/v1beta2"
	"gotest.tools/assert"
	is "gotest.tools/assert/cmp"
	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestNewStackConverter(t *testing.T) {
	_, err := NewStackConverter("v1alpha1")
	assert.Check(t, is.ErrorContains(err, "stack version v1alpha1 unsupported"))

	_, err = NewStackConverter("v1beta1")
	assert.NilError(t, err)
	_, err = NewStackConverter("v1beta2")
	assert.NilError(t, err)
	_, err = NewStackConverter("v1alpha3")
	assert.NilError(t, err)
}

func TestConvertFromToV1beta1(t *testing.T) {
	composefile := `version: "3.3"
services:
  test:
    image: nginx
secrets:
  test:
    file: testdata/secret
configs:
  test:
    file: testdata/config
`
	stackv1beta1 := &v1beta1.Stack{
		ObjectMeta: metav1.ObjectMeta{
			Name: "test",
		},
		Spec: v1beta1.StackSpec{
			ComposeFile: composefile,
		},
	}

	result, err := stackFromV1beta1(stackv1beta1)
	assert.NilError(t, err)
	expected := Stack{
		Name:        "test",
		ComposeFile: composefile,
		Spec: &v1alpha3.StackSpec{
			Services: []v1alpha3.ServiceConfig{
				{
					Name:        "test",
					Image:       "nginx",
					Environment: make(map[string]*string),
				},
			},
			Secrets: map[string]v1alpha3.SecretConfig{
				"test": {File: filepath.FromSlash("testdata/secret")},
			},
			Configs: map[string]v1alpha3.ConfigObjConfig{
				"test": {File: filepath.FromSlash("testdata/config")},
			},
		},
	}
	assert.DeepEqual(t, expected, result)
	assert.DeepEqual(t, stackv1beta1, stackToV1beta1(result))
}

func TestConvertFromToV1beta2(t *testing.T) {
	stackv1beta2 := &v1beta2.Stack{
		ObjectMeta: metav1.ObjectMeta{
			Name: "test",
		},
		Spec: &v1beta2.StackSpec{
			Services: []v1beta2.ServiceConfig{
				{
					Name:        "test",
					Image:       "nginx",
					Environment: make(map[string]*string),
				},
			},
			Secrets: map[string]v1beta2.SecretConfig{
				"test": {File: filepath.FromSlash("testdata/secret")},
			},
			Configs: map[string]v1beta2.ConfigObjConfig{
				"test": {File: filepath.FromSlash("testdata/config")},
			},
		},
	}
	expected := Stack{
		Name: "test",
		Spec: &v1alpha3.StackSpec{
			Services: []v1alpha3.ServiceConfig{
				{
					Name:        "test",
					Image:       "nginx",
					Environment: make(map[string]*string),
				},
			},
			Secrets: map[string]v1alpha3.SecretConfig{
				"test": {File: filepath.FromSlash("testdata/secret")},
			},
			Configs: map[string]v1alpha3.ConfigObjConfig{
				"test": {File: filepath.FromSlash("testdata/config")},
			},
		},
	}
	result, err := stackFromV1beta2(stackv1beta2)
	assert.NilError(t, err)
	assert.DeepEqual(t, expected, result)
	gotBack, err := stackToV1beta2(result)
	assert.NilError(t, err)
	assert.DeepEqual(t, stackv1beta2, gotBack)
}

func TestConvertFromToV1alpha3(t *testing.T) {
	stackv1alpha3 := &v1alpha3.Stack{
		ObjectMeta: metav1.ObjectMeta{
			Name: "test",
		},
		Spec: &v1alpha3.StackSpec{
			Services: []v1alpha3.ServiceConfig{
				{
					Name:        "test",
					Image:       "nginx",
					Environment: make(map[string]*string),
				},
			},
			Secrets: map[string]v1alpha3.SecretConfig{
				"test": {File: filepath.FromSlash("testdata/secret")},
			},
			Configs: map[string]v1alpha3.ConfigObjConfig{
				"test": {File: filepath.FromSlash("testdata/config")},
			},
		},
	}
	expected := Stack{
		Name: "test",
		Spec: &v1alpha3.StackSpec{
			Services: []v1alpha3.ServiceConfig{
				{
					Name:        "test",
					Image:       "nginx",
					Environment: make(map[string]*string),
				},
			},
			Secrets: map[string]v1alpha3.SecretConfig{
				"test": {File: filepath.FromSlash("testdata/secret")},
			},
			Configs: map[string]v1alpha3.ConfigObjConfig{
				"test": {File: filepath.FromSlash("testdata/config")},
			},
		},
	}
	result := stackFromV1alpha3(stackv1alpha3)
	assert.DeepEqual(t, expected, result)
	gotBack := stackToV1alpha3(result)
	assert.DeepEqual(t, stackv1alpha3, gotBack)
}

func loadTestStackWith(t *testing.T, with string) *composetypes.Config {
	t.Helper()
	filePath := fmt.Sprintf("testdata/compose-with-%s.yml", with)
	data, err := ioutil.ReadFile(filePath)
	assert.NilError(t, err)
	yamlData, err := loader.ParseYAML(data)
	assert.NilError(t, err)
	cfg, err := loader.Load(composetypes.ConfigDetails{
		ConfigFiles: []composetypes.ConfigFile{
			{Config: yamlData, Filename: filePath},
		},
	})
	assert.NilError(t, err)
	return cfg
}

func TestHandlePullSecret(t *testing.T) {
	testData := loadTestStackWith(t, "pull-secret")
	cases := []struct {
		version string
		err     string
	}{
		{version: "v1beta1", err: `stack API version v1beta1 does not support pull secrets (field "x-kubernetes.pull_secret"), please use version v1alpha3 or higher`},
		{version: "v1beta2", err: `stack API version v1beta2 does not support pull secrets (field "x-kubernetes.pull_secret"), please use version v1alpha3 or higher`},
		{version: "v1alpha3"},
	}

	for _, c := range cases {
		t.Run(c.version, func(t *testing.T) {
			conv, err := NewStackConverter(c.version)
			assert.NilError(t, err)
			s, err := conv.FromCompose(ioutil.Discard, "test", testData)
			if c.err != "" {
				assert.Error(t, err, c.err)

			} else {
				assert.NilError(t, err)
				assert.Equal(t, s.Spec.Services[0].PullSecret, "some-secret")
			}
		})
	}
}

func TestHandlePullPolicy(t *testing.T) {
	testData := loadTestStackWith(t, "pull-policy")
	cases := []struct {
		version string
		err     string
	}{
		{version: "v1beta1", err: `stack API version v1beta1 does not support pull policies (field "x-kubernetes.pull_policy"), please use version v1alpha3 or higher`},
		{version: "v1beta2", err: `stack API version v1beta2 does not support pull policies (field "x-kubernetes.pull_policy"), please use version v1alpha3 or higher`},
		{version: "v1alpha3"},
	}

	for _, c := range cases {
		t.Run(c.version, func(t *testing.T) {
			conv, err := NewStackConverter(c.version)
			assert.NilError(t, err)
			s, err := conv.FromCompose(ioutil.Discard, "test", testData)
			if c.err != "" {
				assert.Error(t, err, c.err)

			} else {
				assert.NilError(t, err)
				assert.Equal(t, s.Spec.Services[0].PullPolicy, "Never")
			}
		})
	}
}

func TestHandleInternalServiceType(t *testing.T) {
	cases := []struct {
		name     string
		value    string
		caps     composeCapabilities
		err      string
		expected v1alpha3.InternalServiceType
	}{
		{
			name:  "v1beta1",
			value: "ClusterIP",
			caps:  v1beta1Capabilities,
			err:   `stack API version v1beta1 does not support intra-stack load balancing (field "x-kubernetes.internal_service_type"), please use version v1alpha3 or higher`,
		},
		{
			name:  "v1beta2",
			value: "ClusterIP",
			caps:  v1beta2Capabilities,
			err:   `stack API version v1beta2 does not support intra-stack load balancing (field "x-kubernetes.internal_service_type"), please use version v1alpha3 or higher`,
		},
		{
			name:     "v1alpha3",
			value:    "ClusterIP",
			caps:     v1alpha3Capabilities,
			expected: v1alpha3.InternalServiceTypeClusterIP,
		},
		{
			name:  "v1alpha3-invalid",
			value: "invalid",
			caps:  v1alpha3Capabilities,
			err:   `invalid value "invalid" for field "x-kubernetes.internal_service_type", valid values are "ClusterIP" or "Headless"`,
		},
	}
	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			res, err := fromComposeServiceConfig(composetypes.ServiceConfig{
				Name:  "test",
				Image: "test",
				Extras: map[string]interface{}{
					"x-kubernetes": map[string]interface{}{
						"internal_service_type": c.value,
					},
				},
			}, c.caps)
			if c.err == "" {
				assert.NilError(t, err)
				assert.Equal(t, res.InternalServiceType, c.expected)
			} else {
				assert.ErrorContains(t, err, c.err)
			}
		})
	}
}

func TestIgnoreExpose(t *testing.T) {
	testData := loadTestStackWith(t, "expose")
	for _, version := range []string{"v1beta1", "v1beta2"} {
		conv, err := NewStackConverter(version)
		assert.NilError(t, err)
		s, err := conv.FromCompose(ioutil.Discard, "test", testData)
		assert.NilError(t, err)
		assert.Equal(t, len(s.Spec.Services[0].InternalPorts), 0)
	}
}

func TestParseExpose(t *testing.T) {
	testData := loadTestStackWith(t, "expose")
	conv, err := NewStackConverter("v1alpha3")
	assert.NilError(t, err)
	s, err := conv.FromCompose(ioutil.Discard, "test", testData)
	assert.NilError(t, err)
	expected := []v1alpha3.InternalPort{
		{
			Port:     1,
			Protocol: v1.ProtocolTCP,
		},
		{
			Port:     2,
			Protocol: v1.ProtocolTCP,
		},
		{
			Port:     3,
			Protocol: v1.ProtocolTCP,
		},
		{
			Port:     4,
			Protocol: v1.ProtocolTCP,
		},
		{
			Port:     5,
			Protocol: v1.ProtocolUDP,
		},
		{
			Port:     6,
			Protocol: v1.ProtocolUDP,
		},
		{
			Port:     7,
			Protocol: v1.ProtocolUDP,
		},
		{
			Port:     8,
			Protocol: v1.ProtocolUDP,
		},
	}
	assert.DeepEqual(t, s.Spec.Services[0].InternalPorts, expected)
}
<commit_msg>cli/command/stack/kubernetes: Using the variable on range scope `c` in function literal (scopelint)<commit_after>package kubernetes

import (
	"fmt"
	"io/ioutil"
	"path/filepath"
	"testing"

	"github.com/docker/cli/cli/compose/loader"
	composetypes "github.com/docker/cli/cli/compose/types"
	"github.com/docker/compose-on-kubernetes/api/compose/v1alpha3"
	"github.com/docker/compose-on-kubernetes/api/compose/v1beta1"
	"github.com/docker/compose-on-kubernetes/api/compose/v1beta2"
	"gotest.tools/assert"
	is "gotest.tools/assert/cmp"
	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestNewStackConverter(t *testing.T) {
	_, err := NewStackConverter("v1alpha1")
	assert.Check(t, is.ErrorContains(err, "stack version v1alpha1 unsupported"))

	_, err = NewStackConverter("v1beta1")
	assert.NilError(t, err)
	_, err = NewStackConverter("v1beta2")
	assert.NilError(t, err)
	_, err = NewStackConverter("v1alpha3")
	assert.NilError(t, err)
}

func TestConvertFromToV1beta1(t *testing.T) {
	composefile := `version: "3.3"
services:
  test:
    image: nginx
secrets:
  test:
    file: testdata/secret
configs:
  test:
    file: testdata/config
`
	stackv1beta1 := &v1beta1.Stack{
		ObjectMeta: metav1.ObjectMeta{
			Name: "test",
		},
		Spec: v1beta1.StackSpec{
			ComposeFile: composefile,
		},
	}

	result, err := stackFromV1beta1(stackv1beta1)
	assert.NilError(t, err)
	expected := Stack{
		Name:        "test",
		ComposeFile: composefile,
		Spec: &v1alpha3.StackSpec{
			Services: []v1alpha3.ServiceConfig{
				{
					Name:        "test",
					Image:       "nginx",
					Environment: make(map[string]*string),
				},
			},
			Secrets: map[string]v1alpha3.SecretConfig{
				"test": {File: filepath.FromSlash("testdata/secret")},
			},
			Configs: map[string]v1alpha3.ConfigObjConfig{
				"test": {File: filepath.FromSlash("testdata/config")},
			},
		},
	}
	assert.DeepEqual(t, expected, result)
	assert.DeepEqual(t, stackv1beta1, stackToV1beta1(result))
}

func TestConvertFromToV1beta2(t *testing.T) {
	stackv1beta2 := &v1beta2.Stack{
		ObjectMeta: metav1.ObjectMeta{
			Name: "test",
		},
		Spec: &v1beta2.StackSpec{
			Services: []v1beta2.ServiceConfig{
				{
					Name:        "test",
					Image:       "nginx",
					Environment: make(map[string]*string),
				},
			},
			Secrets: map[string]v1beta2.SecretConfig{
				"test": {File: filepath.FromSlash("testdata/secret")},
			},
			Configs: map[string]v1beta2.ConfigObjConfig{
				"test": {File: filepath.FromSlash("testdata/config")},
			},
		},
	}
	expected := Stack{
		Name: "test",
		Spec: &v1alpha3.StackSpec{
			Services: []v1alpha3.ServiceConfig{
				{
					Name:        "test",
					Image:       "nginx",
					Environment: make(map[string]*string),
				},
			},
			Secrets: map[string]v1alpha3.SecretConfig{
				"test": {File: filepath.FromSlash("testdata/secret")},
			},
			Configs: map[string]v1alpha3.ConfigObjConfig{
				"test": {File: filepath.FromSlash("testdata/config")},
			},
		},
	}
	result, err := stackFromV1beta2(stackv1beta2)
	assert.NilError(t, err)
	assert.DeepEqual(t, expected, result)
	gotBack, err := stackToV1beta2(result)
	assert.NilError(t, err)
	assert.DeepEqual(t, stackv1beta2, gotBack)
}

func TestConvertFromToV1alpha3(t *testing.T) {
	stackv1alpha3 := &v1alpha3.Stack{
		ObjectMeta: metav1.ObjectMeta{
			Name: "test",
		},
		Spec: &v1alpha3.StackSpec{
			Services: []v1alpha3.ServiceConfig{
				{
					Name:        "test",
					Image:       "nginx",
					Environment: make(map[string]*string),
				},
			},
			Secrets: map[string]v1alpha3.SecretConfig{
				"test": {File: filepath.FromSlash("testdata/secret")},
			},
			Configs: map[string]v1alpha3.ConfigObjConfig{
				"test": {File: filepath.FromSlash("testdata/config")},
			},
		},
	}
	expected := Stack{
		Name: "test",
		Spec: &v1alpha3.StackSpec{
			Services: []v1alpha3.ServiceConfig{
				{
					Name:        "test",
					Image:       "nginx",
					Environment: make(map[string]*string),
				},
			},
			Secrets: map[string]v1alpha3.SecretConfig{
				"test": {File: filepath.FromSlash("testdata/secret")},
			},
			Configs: map[string]v1alpha3.ConfigObjConfig{
				"test": {File: filepath.FromSlash("testdata/config")},
			},
		},
	}
	result := stackFromV1alpha3(stackv1alpha3)
	assert.DeepEqual(t, expected, result)
	gotBack := stackToV1alpha3(result)
	assert.DeepEqual(t, stackv1alpha3, gotBack)
}

func loadTestStackWith(t *testing.T, with string) *composetypes.Config {
	t.Helper()
	filePath := fmt.Sprintf("testdata/compose-with-%s.yml", with)
	data, err := ioutil.ReadFile(filePath)
	assert.NilError(t, err)
	yamlData, err := loader.ParseYAML(data)
	assert.NilError(t, err)
	cfg, err := loader.Load(composetypes.ConfigDetails{
		ConfigFiles: []composetypes.ConfigFile{
			{Config: yamlData, Filename: filePath},
		},
	})
	assert.NilError(t, err)
	return cfg
}

func TestHandlePullSecret(t *testing.T) {
	testData := loadTestStackWith(t, "pull-secret")
	cases := []struct {
		version string
		err     string
	}{
		{version: "v1beta1", err: `stack API version v1beta1 does not support pull secrets (field "x-kubernetes.pull_secret"), please use version v1alpha3 or higher`},
		{version: "v1beta2", err: `stack API version v1beta2 does not support pull secrets (field "x-kubernetes.pull_secret"), please use version v1alpha3 or higher`},
		{version: "v1alpha3"},
	}

	for _, c := range cases {
		c := c
		t.Run(c.version, func(t *testing.T) {
			conv, err := NewStackConverter(c.version)
			assert.NilError(t, err)
			s, err := conv.FromCompose(ioutil.Discard, "test", testData)
			if c.err != "" {
				assert.Error(t, err, c.err)

			} else {
				assert.NilError(t, err)
				assert.Equal(t, s.Spec.Services[0].PullSecret, "some-secret")
			}
		})
	}
}

func TestHandlePullPolicy(t *testing.T) {
	testData := loadTestStackWith(t, "pull-policy")
	cases := []struct {
		version string
		err     string
	}{
		{version: "v1beta1", err: `stack API version v1beta1 does not support pull policies (field "x-kubernetes.pull_policy"), please use version v1alpha3 or higher`},
		{version: "v1beta2", err: `stack API version v1beta2 does not support pull policies (field "x-kubernetes.pull_policy"), please use version v1alpha3 or higher`},
		{version: "v1alpha3"},
	}

	for _, c := range cases {
		c := c
		t.Run(c.version, func(t *testing.T) {
			conv, err := NewStackConverter(c.version)
			assert.NilError(t, err)
			s, err := conv.FromCompose(ioutil.Discard, "test", testData)
			if c.err != "" {
				assert.Error(t, err, c.err)

			} else {
				assert.NilError(t, err)
				assert.Equal(t, s.Spec.Services[0].PullPolicy, "Never")
			}
		})
	}
}

func TestHandleInternalServiceType(t *testing.T) {
	cases := []struct {
		name     string
		value    string
		caps     composeCapabilities
		err      string
		expected v1alpha3.InternalServiceType
	}{
		{
			name:  "v1beta1",
			value: "ClusterIP",
			caps:  v1beta1Capabilities,
			err:   `stack API version v1beta1 does not support intra-stack load balancing (field "x-kubernetes.internal_service_type"), please use version v1alpha3 or higher`,
		},
		{
			name:  "v1beta2",
			value: "ClusterIP",
			caps:  v1beta2Capabilities,
			err:   `stack API version v1beta2 does not support intra-stack load balancing (field "x-kubernetes.internal_service_type"), please use version v1alpha3 or higher`,
		},
		{
			name:     "v1alpha3",
			value:    "ClusterIP",
			caps:     v1alpha3Capabilities,
			expected: v1alpha3.InternalServiceTypeClusterIP,
		},
		{
			name:  "v1alpha3-invalid",
			value: "invalid",
			caps:  v1alpha3Capabilities,
			err:   `invalid value "invalid" for field "x-kubernetes.internal_service_type", valid values are "ClusterIP" or "Headless"`,
		},
	}
	for _, c := range cases {
		c := c
		t.Run(c.name, func(t *testing.T) {
			res, err := fromComposeServiceConfig(composetypes.ServiceConfig{
				Name:  "test",
				Image: "test",
				Extras: map[string]interface{}{
					"x-kubernetes": map[string]interface{}{
						"internal_service_type": c.value,
					},
				},
			}, c.caps)
			if c.err == "" {
				assert.NilError(t, err)
				assert.Equal(t, res.InternalServiceType, c.expected)
			} else {
				assert.ErrorContains(t, err, c.err)
			}
		})
	}
}

func TestIgnoreExpose(t *testing.T) {
	testData := loadTestStackWith(t, "expose")
	for _, version := range []string{"v1beta1", "v1beta2"} {
		conv, err := NewStackConverter(version)
		assert.NilError(t, err)
		s, err := conv.FromCompose(ioutil.Discard, "test", testData)
		assert.NilError(t, err)
		assert.Equal(t, len(s.Spec.Services[0].InternalPorts), 0)
	}
}

func TestParseExpose(t *testing.T) {
	testData := loadTestStackWith(t, "expose")
	conv, err := NewStackConverter("v1alpha3")
	assert.NilError(t, err)
	s, err := conv.FromCompose(ioutil.Discard, "test", testData)
	assert.NilError(t, err)
	expected := []v1alpha3.InternalPort{
		{
			Port:     1,
			Protocol: v1.ProtocolTCP,
		},
		{
			Port:     2,
			Protocol: v1.ProtocolTCP,
		},
		{
			Port:     3,
			Protocol: v1.ProtocolTCP,
		},
		{
			Port:     4,
			Protocol: v1.ProtocolTCP,
		},
		{
			Port:     5,
			Protocol: v1.ProtocolUDP,
		},
		{
			Port:     6,
			Protocol: v1.ProtocolUDP,
		},
		{
			Port:     7,
			Protocol: v1.ProtocolUDP,
		},
		{
			Port:     8,
			Protocol: v1.ProtocolUDP,
		},
	}
	assert.DeepEqual(t, s.Spec.Services[0].InternalPorts, expected)
}
<|endoftext|>


<commit_before>package blobclient

import (
	"errors"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"sync"

	"code.uber.internal/infra/kraken/core"
	"code.uber.internal/infra/kraken/lib/hostlist"
	"code.uber.internal/infra/kraken/utils/backoff"
	"code.uber.internal/infra/kraken/utils/errutil"
	"code.uber.internal/infra/kraken/utils/httputil"
	"code.uber.internal/infra/kraken/utils/log"
)

// Locations queries cluster for the locations of d.
func Locations(p Provider, cluster hostlist.List, d core.Digest) (locs []string, err error) {
	addrs := cluster.Resolve().Sample(3)
	if len(addrs) == 0 {
		return nil, errors.New("cluster is empty")
	}
	for addr := range addrs {
		locs, err = p.Provide(addr).Locations(d)
		if err != nil {
			continue
		}
		break
	}
	return locs, err
}

// ClientResolver resolves digests into Clients of origins.
type ClientResolver interface {
	// Resolve must return an ordered, stable list of Clients for origins owning d.
	Resolve(d core.Digest) ([]Client, error)
}

type clientResolver struct {
	provider Provider
	cluster  hostlist.List
}

// NewClientResolver returns a new client resolver.
func NewClientResolver(p Provider, cluster hostlist.List) ClientResolver {
	return &clientResolver{p, cluster}
}

func (r *clientResolver) Resolve(d core.Digest) ([]Client, error) {
	locs, err := Locations(r.provider, r.cluster, d)
	if err != nil {
		return nil, err
	}
	var clients []Client
	for _, loc := range locs {
		clients = append(clients, r.provider.Provide(loc))
	}
	return clients, nil
}

// ClusterClient defines a top-level origin cluster client which handles blob
// location resolution and retries.
type ClusterClient interface {
	UploadBlob(namespace string, d core.Digest, blob io.Reader) error
	DownloadBlob(namespace string, d core.Digest, dst io.Writer) error
	GetMetaInfo(namespace string, d core.Digest) (*core.MetaInfo, error)
	Stat(namespace string, d core.Digest) (*core.BlobInfo, error)
	OverwriteMetaInfo(d core.Digest, pieceLength int64) error
	Owners(d core.Digest) ([]core.PeerContext, error)
	ReplicateToRemote(namespace string, d core.Digest, remoteDNS string) error
}

type clusterClient struct {
	resolver            ClientResolver
	pollDownloadBackoff *backoff.Backoff
}

// NewClusterClient returns a new ClusterClient.
func NewClusterClient(r ClientResolver) ClusterClient {
	return &clusterClient{
		resolver:            r,
		pollDownloadBackoff: backoff.New(backoff.Config{}),
	}
}

// UploadBlob uploads blob to origin cluster. See Client.UploadBlob for more details.
func (c *clusterClient) UploadBlob(namespace string, d core.Digest, blob io.Reader) (err error) {
	clients, err := c.resolver.Resolve(d)
	if err != nil {
		return fmt.Errorf("resolve clients: %s", err)
	}

	// We prefer the origin with highest hashing score so the first origin will handle
	// replication to origins with lower score. This is because we want to reduce upload
	// conflicts between local replicas.
	for _, client := range clients {
		err = client.UploadBlob(namespace, d, blob)
		if httputil.IsNetworkError(err) {
			continue
		}
		break
	}
	return err
}

// GetMetaInfo returns the metainfo for d. Does not handle polling.
func (c *clusterClient) GetMetaInfo(namespace string, d core.Digest) (mi *core.MetaInfo, err error) {
	clients, err := c.resolver.Resolve(d)
	if err != nil {
		return nil, fmt.Errorf("resolve clients: %s", err)
	}
	for _, client := range clients {
		mi, err = client.GetMetaInfo(namespace, d)
		if err != nil {
			continue
		}
		break
	}
	return mi, err
}

// Stat checks availability of a blob in the cluster.
func (c *clusterClient) Stat(namespace string, d core.Digest) (bi *core.BlobInfo, err error) {
	clients, err := c.resolver.Resolve(d)
	if err != nil {
		return nil, fmt.Errorf("resolve clients: %s", err)
	}

	shuffle(clients)
	for _, client := range clients {
		bi, err = client.Stat(namespace, d)
		if err != nil {
			continue
		}
		break
	}

	return bi, err
}

// OverwriteMetaInfo overwrites existing metainfo for d with new metainfo configured
// with pieceLength on every origin server. Returns error if any origin was unable
// to overwrite metainfo. Primarly intended for benchmarking purposes.
func (c *clusterClient) OverwriteMetaInfo(d core.Digest, pieceLength int64) error {
	clients, err := c.resolver.Resolve(d)
	if err != nil {
		return fmt.Errorf("resolve clients: %s", err)
	}
	var errs []error
	for _, client := range clients {
		if err := client.OverwriteMetaInfo(d, pieceLength); err != nil {
			errs = append(errs, fmt.Errorf("origin %s: %s", client.Addr(), err))
		}
	}
	return errutil.Join(errs)
}

// DownloadBlob pulls a blob from the origin cluster.
func (c *clusterClient) DownloadBlob(namespace string, d core.Digest, dst io.Writer) error {
	err := Poll(c.resolver, c.pollDownloadBackoff, d, func(client Client) error {
		return client.DownloadBlob(namespace, d, dst)
	})
	if httputil.IsNotFound(err) {
		err = ErrBlobNotFound
	}
	return err
}

// Owners returns the origin peers which own d.
func (c *clusterClient) Owners(d core.Digest) ([]core.PeerContext, error) {
	clients, err := c.resolver.Resolve(d)
	if err != nil {
		return nil, fmt.Errorf("resolve clients: %s", err)
	}

	var mu sync.Mutex
	var peers []core.PeerContext
	var errs []error

	var wg sync.WaitGroup
	for _, client := range clients {
		wg.Add(1)
		go func(client Client) {
			defer wg.Done()
			pctx, err := client.GetPeerContext()
			mu.Lock()
			if err != nil {
				errs = append(errs, err)
			} else {
				peers = append(peers, pctx)
			}
			mu.Unlock()
		}(client)
	}
	wg.Wait()

	err = errutil.Join(errs)

	if len(peers) == 0 {
		if err != nil {
			return nil, err
		}
		return nil, errors.New("no origin peers found")
	}

	if err != nil {
		log.With("blob", d.Hex()).Errorf("Error getting all origin peers: %s", err)
	}
	return peers, nil
}

// ReplicateToRemote replicates d to a remote origin cluster.
func (c *clusterClient) ReplicateToRemote(namespace string, d core.Digest, remoteDNS string) error {
	// Re-use download backoff since replicate may download blobs.
	return Poll(c.resolver, c.pollDownloadBackoff, d, func(client Client) error {
		return client.ReplicateToRemote(namespace, d, remoteDNS)
	})
}

func shuffle(cs []Client) {
	for i := range cs {
		j := rand.Intn(i + 1)
		cs[i], cs[j] = cs[j], cs[i]
	}
}

// Poll wraps requests for endpoints which require polling, due to a blob
// being asynchronously fetched from remote storage in the origin cluster.
func Poll(
	r ClientResolver, b *backoff.Backoff, d core.Digest, makeRequest func(Client) error) error {

	// By looping over clients in order, we will always prefer the same origin
	// for making requests to loosely guarantee that only one origin needs to
	// fetch the file from remote backend.
	clients, err := r.Resolve(d)
	if err != nil {
		return fmt.Errorf("resolve clients: %s", err)
	}
	var errs []error
ORIGINS:
	for _, client := range clients {
		a := b.Attempts()
	POLL:
		for a.WaitForNext() {
			if err := makeRequest(client); err != nil {
				if serr, ok := err.(httputil.StatusError); ok {
					if serr.Status == http.StatusAccepted {
						continue POLL
					}
					if serr.Status < 500 {
						return err
					}
				}
				errs = append(errs, fmt.Errorf("origin %s: %s", client.Addr(), err))
				continue ORIGINS
			}
			return nil // Success!
		}
		errs = append(errs, fmt.Errorf("origin %s: 202 backoff: %s", client.Addr(), a.Err()))
	}
	return fmt.Errorf("all origins unavailable: %s", errutil.Join(errs))
}
<commit_msg>Short circuit ClusterClient.GetMetaInfo on 202<commit_after>package blobclient

import (
	"errors"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"sync"

	"code.uber.internal/infra/kraken/core"
	"code.uber.internal/infra/kraken/lib/hostlist"
	"code.uber.internal/infra/kraken/utils/backoff"
	"code.uber.internal/infra/kraken/utils/errutil"
	"code.uber.internal/infra/kraken/utils/httputil"
	"code.uber.internal/infra/kraken/utils/log"
)

// Locations queries cluster for the locations of d.
func Locations(p Provider, cluster hostlist.List, d core.Digest) (locs []string, err error) {
	addrs := cluster.Resolve().Sample(3)
	if len(addrs) == 0 {
		return nil, errors.New("cluster is empty")
	}
	for addr := range addrs {
		locs, err = p.Provide(addr).Locations(d)
		if err != nil {
			continue
		}
		break
	}
	return locs, err
}

// ClientResolver resolves digests into Clients of origins.
type ClientResolver interface {
	// Resolve must return an ordered, stable list of Clients for origins owning d.
	Resolve(d core.Digest) ([]Client, error)
}

type clientResolver struct {
	provider Provider
	cluster  hostlist.List
}

// NewClientResolver returns a new client resolver.
func NewClientResolver(p Provider, cluster hostlist.List) ClientResolver {
	return &clientResolver{p, cluster}
}

func (r *clientResolver) Resolve(d core.Digest) ([]Client, error) {
	locs, err := Locations(r.provider, r.cluster, d)
	if err != nil {
		return nil, err
	}
	var clients []Client
	for _, loc := range locs {
		clients = append(clients, r.provider.Provide(loc))
	}
	return clients, nil
}

// ClusterClient defines a top-level origin cluster client which handles blob
// location resolution and retries.
type ClusterClient interface {
	UploadBlob(namespace string, d core.Digest, blob io.Reader) error
	DownloadBlob(namespace string, d core.Digest, dst io.Writer) error
	GetMetaInfo(namespace string, d core.Digest) (*core.MetaInfo, error)
	Stat(namespace string, d core.Digest) (*core.BlobInfo, error)
	OverwriteMetaInfo(d core.Digest, pieceLength int64) error
	Owners(d core.Digest) ([]core.PeerContext, error)
	ReplicateToRemote(namespace string, d core.Digest, remoteDNS string) error
}

type clusterClient struct {
	resolver            ClientResolver
	pollDownloadBackoff *backoff.Backoff
}

// NewClusterClient returns a new ClusterClient.
func NewClusterClient(r ClientResolver) ClusterClient {
	return &clusterClient{
		resolver:            r,
		pollDownloadBackoff: backoff.New(backoff.Config{}),
	}
}

// UploadBlob uploads blob to origin cluster. See Client.UploadBlob for more details.
func (c *clusterClient) UploadBlob(namespace string, d core.Digest, blob io.Reader) (err error) {
	clients, err := c.resolver.Resolve(d)
	if err != nil {
		return fmt.Errorf("resolve clients: %s", err)
	}

	// We prefer the origin with highest hashing score so the first origin will handle
	// replication to origins with lower score. This is because we want to reduce upload
	// conflicts between local replicas.
	for _, client := range clients {
		err = client.UploadBlob(namespace, d, blob)
		if httputil.IsNetworkError(err) {
			continue
		}
		break
	}
	return err
}

// GetMetaInfo returns the metainfo for d. Does not handle polling.
func (c *clusterClient) GetMetaInfo(namespace string, d core.Digest) (mi *core.MetaInfo, err error) {
	clients, err := c.resolver.Resolve(d)
	if err != nil {
		return nil, fmt.Errorf("resolve clients: %s", err)
	}
	for _, client := range clients {
		mi, err = client.GetMetaInfo(namespace, d)
		// Do not try the next replica on 202 errors.
		if err != nil && !httputil.IsAccepted(err) {
			continue
		}
		break
	}
	return mi, err
}

// Stat checks availability of a blob in the cluster.
func (c *clusterClient) Stat(namespace string, d core.Digest) (bi *core.BlobInfo, err error) {
	clients, err := c.resolver.Resolve(d)
	if err != nil {
		return nil, fmt.Errorf("resolve clients: %s", err)
	}

	shuffle(clients)
	for _, client := range clients {
		bi, err = client.Stat(namespace, d)
		if err != nil {
			continue
		}
		break
	}

	return bi, err
}

// OverwriteMetaInfo overwrites existing metainfo for d with new metainfo configured
// with pieceLength on every origin server. Returns error if any origin was unable
// to overwrite metainfo. Primarly intended for benchmarking purposes.
func (c *clusterClient) OverwriteMetaInfo(d core.Digest, pieceLength int64) error {
	clients, err := c.resolver.Resolve(d)
	if err != nil {
		return fmt.Errorf("resolve clients: %s", err)
	}
	var errs []error
	for _, client := range clients {
		if err := client.OverwriteMetaInfo(d, pieceLength); err != nil {
			errs = append(errs, fmt.Errorf("origin %s: %s", client.Addr(), err))
		}
	}
	return errutil.Join(errs)
}

// DownloadBlob pulls a blob from the origin cluster.
func (c *clusterClient) DownloadBlob(namespace string, d core.Digest, dst io.Writer) error {
	err := Poll(c.resolver, c.pollDownloadBackoff, d, func(client Client) error {
		return client.DownloadBlob(namespace, d, dst)
	})
	if httputil.IsNotFound(err) {
		err = ErrBlobNotFound
	}
	return err
}

// Owners returns the origin peers which own d.
func (c *clusterClient) Owners(d core.Digest) ([]core.PeerContext, error) {
	clients, err := c.resolver.Resolve(d)
	if err != nil {
		return nil, fmt.Errorf("resolve clients: %s", err)
	}

	var mu sync.Mutex
	var peers []core.PeerContext
	var errs []error

	var wg sync.WaitGroup
	for _, client := range clients {
		wg.Add(1)
		go func(client Client) {
			defer wg.Done()
			pctx, err := client.GetPeerContext()
			mu.Lock()
			if err != nil {
				errs = append(errs, err)
			} else {
				peers = append(peers, pctx)
			}
			mu.Unlock()
		}(client)
	}
	wg.Wait()

	err = errutil.Join(errs)

	if len(peers) == 0 {
		if err != nil {
			return nil, err
		}
		return nil, errors.New("no origin peers found")
	}

	if err != nil {
		log.With("blob", d.Hex()).Errorf("Error getting all origin peers: %s", err)
	}
	return peers, nil
}

// ReplicateToRemote replicates d to a remote origin cluster.
func (c *clusterClient) ReplicateToRemote(namespace string, d core.Digest, remoteDNS string) error {
	// Re-use download backoff since replicate may download blobs.
	return Poll(c.resolver, c.pollDownloadBackoff, d, func(client Client) error {
		return client.ReplicateToRemote(namespace, d, remoteDNS)
	})
}

func shuffle(cs []Client) {
	for i := range cs {
		j := rand.Intn(i + 1)
		cs[i], cs[j] = cs[j], cs[i]
	}
}

// Poll wraps requests for endpoints which require polling, due to a blob
// being asynchronously fetched from remote storage in the origin cluster.
func Poll(
	r ClientResolver, b *backoff.Backoff, d core.Digest, makeRequest func(Client) error) error {

	// By looping over clients in order, we will always prefer the same origin
	// for making requests to loosely guarantee that only one origin needs to
	// fetch the file from remote backend.
	clients, err := r.Resolve(d)
	if err != nil {
		return fmt.Errorf("resolve clients: %s", err)
	}
	var errs []error
ORIGINS:
	for _, client := range clients {
		a := b.Attempts()
	POLL:
		for a.WaitForNext() {
			if err := makeRequest(client); err != nil {
				if serr, ok := err.(httputil.StatusError); ok {
					if serr.Status == http.StatusAccepted {
						continue POLL
					}
					if serr.Status < 500 {
						return err
					}
				}
				errs = append(errs, fmt.Errorf("origin %s: %s", client.Addr(), err))
				continue ORIGINS
			}
			return nil // Success!
		}
		errs = append(errs, fmt.Errorf("origin %s: 202 backoff: %s", client.Addr(), a.Err()))
	}
	return fmt.Errorf("all origins unavailable: %s", errutil.Join(errs))
}
<|endoftext|>


<commit_before>package swap

import "github.com/catorpilor/leetcode/utils"

func swapPairs(head *utils.ListNode) *utils.ListNode {
	if head == nil {
		return nil
	}
	p1, p2 := head, head.Next
	var temp int
	for p2 != nil {
		// swap adjacent
		temp = p1.Val
		p1.Val = p2.Val
		p2.Val = temp

		// update pointer
		p1 = p2.Next
		if p1 != nil && p1.Next != nil {
			p2 = p1.Next
		} else {
			p2 = nil
		}

	}

	return head
}
<commit_msg>solve 24 by swaping the nodes<commit_after>package swap

import "github.com/catorpilor/leetcode/utils"

func swapPairs(head *utils.ListNode) *utils.ListNode {
	return swapNodes(head)
}

// swapByValue just swap values instead, which is not allowed
func swapByValue(head *utils.ListNode) *utils.ListNode {
	if head == nil {
		return nil
	}
	p1, p2 := head, head.Next
	var temp int
	for p2 != nil {
		// swap adjacent
		temp = p1.Val
		p1.Val = p2.Val
		p2.Val = temp

		// update pointer
		p1 = p2.Next
		if p1 != nil && p1.Next != nil {
			p2 = p1.Next
		} else {
			p2 = nil
		}

	}

	return head
}

// swapNodes time complexity O(N), space complexity O(1)
func swapNodes(head *utils.ListNode) *utils.ListNode {
	if head == nil {
		return head
	}
	dummy := &utils.ListNode{Next: head}
	prev := dummy
	for head != nil && head.Next != nil {
		first, second := head, head.Next
		head = second.Next
		first.Next = head
		prev.Next = second
		second.Next = first
		prev = first
	}
	return dummy.Next
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/hex"
	"errors"
	"github.com/digitalocean/godo"
	"golang.org/x/oauth2"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"
)

var doClient *godo.Client
var doToken string
var doTokenBytes []byte
var stateLock *sync.Mutex = &sync.Mutex{}

var ErrNotRunning = errors.New("dynamicserver: server not running")
var ErrUnexpected = errors.New("dynamicserver: unexpected error")
var ErrSafetyCheckFail = errors.New("dynamicserver: failed safety check")

const (
	dropletStateUnknown = iota
	dropletStateActive
	dropletStateSnapshot
	dropletStateShuttingDown
	dropletStateDestroy
	dropletStateCreate
	dropletStateOff
)

type dropletInfo struct {
	id           int
	name         string
	ipAddress    string
	currentState int
}

type snapshotInfo struct {
	time int64
	id   int
}

type TokenSource struct {
	AccessToken string
}

func (t *TokenSource) Token() (*oauth2.Token, error) {
	token := &oauth2.Token{
		AccessToken: t.AccessToken,
	}
	return token, nil
}

func loadDoClient() {
	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		log.Fatal(err)
	}

	fileData, err := ioutil.ReadFile(dir + "/token.txt")
	if err != nil {
		log.Fatal(err)
	}

	doToken = strings.Trim(string(fileData), " \n")
	doTokenBytes, err = hex.DecodeString(doToken)
	if err != nil {
		log.Fatal("[Token] WARNING: Failed to decode token!")
	}

	tokenSource := &TokenSource{
		AccessToken: doToken,
	}

	oauthClient := oauth2.NewClient(oauth2.NoContext, tokenSource)
	doClient = godo.NewClient(oauthClient)
}

func shutdownServer() {
	stateLock.Lock()
	defer stateLock.Unlock()

	setState(stateIdling)
	for i := 0; i < 5; i++ {
		droplet, err := getRunningDroplet()
		log.Println("[Shutdown] Attempting to shutdown:", droplet.name)
		if err != nil {
			log.Println("[Shutdown] Failed get droplet information:", err)
			time.Sleep(time.Second * 5)
			continue
		}

		if droplet.currentState == dropletStateShuttingDown ||
			droplet.currentState == dropletStateOff {
			return
		}

		_, _, err = doClient.DropletActions.Shutdown(droplet.id)
		if err != nil {
			log.Println("[Shutdown] Failed to shutdown droplet:", err)
			time.Sleep(time.Second * 5)
			continue
		}

		return
	}
}

func snapshotServer() {
	stateLock.Lock()
	defer stateLock.Unlock()

	setState(stateIdling)
	// Will be followed by a destruction
	for i := 0; i < 5; i++ {
		droplet, err := getRunningDroplet()
		if err != nil {
			log.Println("[Snapshot] Failed to get running droplet:", err)
			continue
		}

		if droplet.currentState == dropletStateSnapshot {
			return
		}

		snapshotTime := time.Now().Unix()
		_, _, err = doClient.DropletActions.Snapshot(droplet.id,
			"minecraft-"+strconv.FormatInt(snapshotTime, 10))
		if err != nil {
			log.Println("[Snapshot] Failed to snapshot droplet:", err)
			continue
		}

		log.Println("[Snapshot] Created snapshot: minecraft-", snapshotTime)

		opt := &godo.ListOptions{
			Page:    1,
			PerPage: 100,
		}

		mcSnapshots := []snapshotInfo{}

		snapshots, _, err := doClient.Images.ListUser(opt)
		if err != nil {
			log.Println("[Snapshot] Failed to list snapshots:", err)
			return
		}
		for _, snapshot := range snapshots {
			if len(snapshot.Name) > 10 && snapshot.Name[:10] == "minecraft-" {
				value, err := strconv.ParseInt(snapshot.Name[10:], 10, 64)
				if err != nil {
					log.Println("[Snapshot] Failed to parse snapshot name: " +
						snapshot.Name)
					continue
				}

				mcSnapshots = append(mcSnapshots,
					snapshotInfo{id: snapshot.ID, time: value})
			}
		}

		for len(mcSnapshots) > 3 {
			// Destroy until only 2 snapshots remain.

			earliestIndex := -1
			earliestSnapshot := snapshotInfo{time: time.Now().Unix(), id: 0}
			for k, snapshot := range mcSnapshots {
				if snapshot.time < earliestSnapshot.time {
					earliestIndex = k
					earliestSnapshot = snapshot
				}
			}

			if earliestIndex >= 0 {
				log.Println("Removing snapshot: minecraft-",
					earliestSnapshot.time)
				_, err := doClient.Images.Delete(earliestSnapshot.id)
				if err != nil {
					log.Println("Failed to remove snapshot:", err)
					break
				}

				mcSnapshots[earliestIndex] = mcSnapshots[len(mcSnapshots)-1]
				mcSnapshots = mcSnapshots[:len(mcSnapshots)-1]
			}
		}

		return
	}

	log.Println("[Snapshot] Failed to snapshot server!")
}

func destroyServer(id int) {
	stateLock.Lock()
	defer stateLock.Unlock()

	setState(stateIdling)
	log.Println("[Destroy] Destroying droplet:", id)

	if id == 3608740 {
		log.Println("[DANGER] SAFETY CHECK FAIL: ATTEMPT TO DESTROY MAIN DROPLET.")
		return
	}

	for i := 0; i < 5; i++ {
		_, err := doClient.Droplets.Delete(id)
		if err != nil {
			log.Println("[Destroy] Error while destroying droplet:", err)
			time.Sleep(time.Second * 5)
			continue
		}

		return
	}
}

func restoreServer() {
	stateLock.Lock()
	defer stateLock.Unlock()

	setState(stateStarting)

	opt := &godo.ListOptions{
		Page:    1,
		PerPage: 100,
	}

	for i := 0; i < 5; i++ {
		// Safety check: Make sure there aren't more than 4 droplets running.
		droplets, _, err := doClient.Droplets.List(opt)
		if err != nil {
			log.Println("[Restore] Failed to get droplet list:", err)
			time.Sleep(time.Second * 5)
			continue
		}

		if len(droplets) > 4 {
			// Refuse to create droplet
			log.Println("[Restore] Too many existing droplets, droplet restoration cancelled.")
			return
		} else {
			break
		}
	}

	var latestSnapshot snapshotInfo

	for i := 0; i < 5; i++ {
		snapshots, _, err := doClient.Images.ListUser(opt)
		if err != nil {
			log.Println("[Restore] Failed to list snapshots:", err)
			time.Sleep(time.Second * 5)
			continue
		}
		for _, snapshot := range snapshots {
			if len(snapshot.Name) > 10 && snapshot.Name[:10] == "minecraft-" {
				value, err := strconv.ParseInt(snapshot.Name[10:], 10, 64)
				if err != nil {
					log.Println("[Restore] Failed to parse snapshot name: " +
						snapshot.Name)
					continue
				}

				if value > latestSnapshot.time {
					latestSnapshot = snapshotInfo{id: snapshot.ID, time: value}
				}
			}
		}
	}

	if latestSnapshot.id == 0 {
		log.Println("[Restore] No valid snapshots found!")
		return
	}

	createRequest := &godo.DropletCreateRequest{
		Name:   "minecraft-" + strconv.FormatInt(time.Now().Unix(), 10),
		Region: "sgp1",
		Size:   "1gb",
		Image: godo.DropletCreateImage{
			ID: latestSnapshot.id,
		},
	}

	for i := 0; i < 5; i++ {
		_, _, err := doClient.Droplets.Create(createRequest)
		if err != nil {
			log.Println("[Restore] Failed to create droplet:", err)
			time.Sleep(time.Second * 5)
			continue
		}
	}
}

func getRunningDroplet() (dropletInfo, error) {
	opt := &godo.ListOptions{
		Page:    1,
		PerPage: 100,
	}

	droplets, _, err := doClient.Droplets.List(opt)
	if err != nil {
		return dropletInfo{}, err
	}

	var runningDroplet godo.Droplet

	for _, droplet := range droplets {
		if len(droplet.Name) > 10 && droplet.Name[:10] == "minecraft-" {
			runningDroplet = droplet
			break
		}
	}

	if runningDroplet.ID == 0 {
		return dropletInfo{}, ErrNotRunning
	}

	runningDropletInfo := dropletInfo{
		id:        runningDroplet.ID,
		name:      runningDroplet.Name,
		ipAddress: runningDroplet.Networks.V4[0].IPAddress,
	}

	actions, _, err := doClient.Droplets.Actions(runningDroplet.ID, opt)
	if err != nil {
		return dropletInfo{}, err
	}

	// Issues with the most recent action takes priority over whether the
	// the droplet is active or not.
	if actions[0].Status == "errored" {
		return runningDropletInfo, ErrUnexpected
	}

	if runningDroplet.Status == "active" {
		runningDropletInfo.currentState = dropletStateActive
		return runningDropletInfo, nil
	}

	if actions[0].Status == "completed" {
		runningDropletInfo.currentState = dropletStateOff
		return runningDropletInfo, nil
	}

	switch actions[0].Type {
	case "create":
		runningDropletInfo.currentState = dropletStateCreate
	case "snapshot":
		runningDropletInfo.currentState = dropletStateSnapshot
	case "destroy":
		runningDropletInfo.currentState = dropletStateDestroy
	case "shutdown":
		runningDropletInfo.currentState = dropletStateShuttingDown
	default:
		runningDropletInfo.currentState = dropletStateUnknown
	}

	return runningDropletInfo, nil
}
<commit_msg>Fix issues with not exiting loops<commit_after>package main

import (
	"encoding/hex"
	"errors"
	"github.com/digitalocean/godo"
	"golang.org/x/oauth2"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"
)

var doClient *godo.Client
var doToken string
var doTokenBytes []byte
var stateLock *sync.Mutex = &sync.Mutex{}

var ErrNotRunning = errors.New("dynamicserver: server not running")
var ErrUnexpected = errors.New("dynamicserver: unexpected error")
var ErrSafetyCheckFail = errors.New("dynamicserver: failed safety check")

const (
	dropletStateUnknown = iota
	dropletStateActive
	dropletStateSnapshot
	dropletStateShuttingDown
	dropletStateDestroy
	dropletStateCreate
	dropletStateOff
)

type dropletInfo struct {
	id           int
	name         string
	ipAddress    string
	currentState int
}

type snapshotInfo struct {
	time int64
	id   int
}

type TokenSource struct {
	AccessToken string
}

func (t *TokenSource) Token() (*oauth2.Token, error) {
	token := &oauth2.Token{
		AccessToken: t.AccessToken,
	}
	return token, nil
}

func loadDoClient() {
	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		log.Fatal(err)
	}

	fileData, err := ioutil.ReadFile(dir + "/token.txt")
	if err != nil {
		log.Fatal(err)
	}

	doToken = strings.Trim(string(fileData), " \n")
	doTokenBytes, err = hex.DecodeString(doToken)
	if err != nil {
		log.Fatal("[Token] WARNING: Failed to decode token!")
	}

	tokenSource := &TokenSource{
		AccessToken: doToken,
	}

	oauthClient := oauth2.NewClient(oauth2.NoContext, tokenSource)
	doClient = godo.NewClient(oauthClient)
}

func shutdownServer() {
	stateLock.Lock()
	defer stateLock.Unlock()

	setState(stateIdling)
	for i := 0; i < 5; i++ {
		droplet, err := getRunningDroplet()
		log.Println("[Shutdown] Attempting to shutdown:", droplet.name)
		if err != nil {
			log.Println("[Shutdown] Failed get droplet information:", err)
			time.Sleep(time.Second * 5)
			continue
		}

		if droplet.currentState == dropletStateShuttingDown ||
			droplet.currentState == dropletStateOff {
			return
		}

		_, _, err = doClient.DropletActions.Shutdown(droplet.id)
		if err != nil {
			log.Println("[Shutdown] Failed to shutdown droplet:", err)
			time.Sleep(time.Second * 5)
			continue
		}

		return
	}
}

func snapshotServer() {
	stateLock.Lock()
	defer stateLock.Unlock()

	setState(stateIdling)
	// Will be followed by a destruction
	for i := 0; i < 5; i++ {
		droplet, err := getRunningDroplet()
		if err != nil {
			log.Println("[Snapshot] Failed to get running droplet:", err)
			continue
		}

		if droplet.currentState == dropletStateSnapshot {
			return
		}

		snapshotTime := time.Now().Unix()
		_, _, err = doClient.DropletActions.Snapshot(droplet.id,
			"minecraft-"+strconv.FormatInt(snapshotTime, 10))
		if err != nil {
			log.Println("[Snapshot] Failed to snapshot droplet:", err)
			continue
		}

		log.Println("[Snapshot] Created snapshot: minecraft-", snapshotTime)

		opt := &godo.ListOptions{
			Page:    1,
			PerPage: 100,
		}

		mcSnapshots := []snapshotInfo{}

		snapshots, _, err := doClient.Images.ListUser(opt)
		if err != nil {
			log.Println("[Snapshot] Failed to list snapshots:", err)
			return
		}
		for _, snapshot := range snapshots {
			if len(snapshot.Name) > 10 && snapshot.Name[:10] == "minecraft-" {
				value, err := strconv.ParseInt(snapshot.Name[10:], 10, 64)
				if err != nil {
					log.Println("[Snapshot] Failed to parse snapshot name: " +
						snapshot.Name)
					continue
				}

				mcSnapshots = append(mcSnapshots,
					snapshotInfo{id: snapshot.ID, time: value})
			}
		}

		for len(mcSnapshots) > 3 {
			// Destroy until only 2 snapshots remain.

			earliestIndex := -1
			earliestSnapshot := snapshotInfo{time: time.Now().Unix(), id: 0}
			for k, snapshot := range mcSnapshots {
				if snapshot.time < earliestSnapshot.time {
					earliestIndex = k
					earliestSnapshot = snapshot
				}
			}

			if earliestIndex >= 0 {
				log.Println("[Snapshot] Removing snapshot: minecraft-",
					earliestSnapshot.time)
				_, err := doClient.Images.Delete(earliestSnapshot.id)
				if err != nil {
					log.Println("[Snapshot] Failed to remove snapshot:", err)
					break
				}

				mcSnapshots[earliestIndex] = mcSnapshots[len(mcSnapshots)-1]
				mcSnapshots = mcSnapshots[:len(mcSnapshots)-1]
			}
		}

		return
	}

	log.Println("[Snapshot] Failed to snapshot server!")
}

func destroyServer(id int) {
	stateLock.Lock()
	defer stateLock.Unlock()

	setState(stateIdling)
	log.Println("[Destroy] Destroying droplet:", id)

	if id == 3608740 {
		log.Println("[DANGER] SAFETY CHECK FAIL: ATTEMPT TO DESTROY MAIN DROPLET.")
		return
	}

	for i := 0; i < 5; i++ {
		_, err := doClient.Droplets.Delete(id)
		if err != nil {
			log.Println("[Destroy] Error while destroying droplet:", err)
			time.Sleep(time.Second * 5)
			continue
		}

		return
	}
}

func restoreServer() {
	stateLock.Lock()
	defer stateLock.Unlock()

	setState(stateStarting)

	opt := &godo.ListOptions{
		Page:    1,
		PerPage: 100,
	}

	var latestSnapshot snapshotInfo

	for i := 0; i < 5; i++ {
		snapshots, _, err := doClient.Images.ListUser(opt)
		if err != nil {
			log.Println("[Restore] Failed to list snapshots:", err)
			time.Sleep(time.Second * 5)
			continue
		}
		for _, snapshot := range snapshots {
			if len(snapshot.Name) > 10 && snapshot.Name[:10] == "minecraft-" {
				value, err := strconv.ParseInt(snapshot.Name[10:], 10, 64)
				if err != nil {
					log.Println("[Restore] Failed to parse snapshot name: " +
						snapshot.Name)
					continue
				}

				if value > latestSnapshot.time {
					latestSnapshot = snapshotInfo{id: snapshot.ID, time: value}
				}
			}
		}

		return
	}

	if latestSnapshot.id == 0 {
		log.Println("[Restore] No valid snapshots found!")
		return
	}

	dropletTime := strconv.FormatInt(time.Now().Unix(), 10)

	createRequest := &godo.DropletCreateRequest{
		Name:   "minecraft-" + dropletTime,
		Region: "sgp1",
		Size:   "1gb",
		Image: godo.DropletCreateImage{
			ID: latestSnapshot.id,
		},
	}

	log.Println("[Restore] Restoring snapshot minecraft-",
		latestSnapshot.time, "as droplet minecraft-"+dropletTime)

	for i := 0; i < 5; i++ {
		// Safety check: Make sure there aren't more than 4 droplets running.
		droplets, _, err := doClient.Droplets.List(opt)
		if err != nil {
			log.Println("[Restore] Failed to get droplet list:", err)
			time.Sleep(time.Second * 5)
			continue
		}

		if len(droplets) > 4 {
			// Refuse to create droplet
			log.Println("[Restore] Too many existing droplets, droplet restoration cancelled.")
			return
		}

		for _, droplet := range droplets {
			if len(droplet.Name) > 10 && droplet.Name[:10] == "minecraft-" {
				log.Println("[Restore] There is an already existing " +
					"minecraft droplet. Waiting and retrying.")
				time.Sleep(time.Second * 10)
				continue
			}
		}

		_, _, err = doClient.Droplets.Create(createRequest)
		if err != nil {
			log.Println("[Restore] Failed to create droplet:", err)
			time.Sleep(time.Second * 5)
			continue
		}

		return
	}
}

func getRunningDroplet() (dropletInfo, error) {
	opt := &godo.ListOptions{
		Page:    1,
		PerPage: 100,
	}

	droplets, _, err := doClient.Droplets.List(opt)
	if err != nil {
		return dropletInfo{}, err
	}

	var runningDroplet godo.Droplet

	for _, droplet := range droplets {
		if len(droplet.Name) > 10 && droplet.Name[:10] == "minecraft-" {
			runningDroplet = droplet
			break
		}
	}

	if runningDroplet.ID == 0 {
		return dropletInfo{}, ErrNotRunning
	}

	runningDropletInfo := dropletInfo{
		id:        runningDroplet.ID,
		name:      runningDroplet.Name,
		ipAddress: runningDroplet.Networks.V4[0].IPAddress,
	}

	actions, _, err := doClient.Droplets.Actions(runningDroplet.ID, opt)
	if err != nil {
		return dropletInfo{}, err
	}

	// Issues with the most recent action takes priority over whether the
	// the droplet is active or not.
	if actions[0].Status == "errored" {
		return runningDropletInfo, ErrUnexpected
	}

	if runningDroplet.Status == "active" {
		runningDropletInfo.currentState = dropletStateActive
		return runningDropletInfo, nil
	}

	if actions[0].Status == "completed" {
		runningDropletInfo.currentState = dropletStateOff
		return runningDropletInfo, nil
	}

	switch actions[0].Type {
	case "create":
		runningDropletInfo.currentState = dropletStateCreate
	case "snapshot":
		runningDropletInfo.currentState = dropletStateSnapshot
	case "destroy":
		runningDropletInfo.currentState = dropletStateDestroy
	case "shutdown":
		runningDropletInfo.currentState = dropletStateShuttingDown
	default:
		runningDropletInfo.currentState = dropletStateUnknown
	}

	return runningDropletInfo, nil
}
<|endoftext|>


<commit_before>// Copyright © 2015 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package components

import (
	"encoding/json"
	"github.com/thethingsnetwork/core/semtech"
	"time"
)

type Metadata struct {
	Chan *uint      `json:"chan,omitempty"` // Concentrator "IF" channel used for RX (unsigned integer)
	Codr *string    `json:"codr,omitempty"` // LoRa ECC coding rate identifier
	Data *string    `json:"data,omitempty"` // Base64 encoded RF packet payload, padded
	Datr *string    `json:"-"`              // FSK datarate (unsigned in bit per second) || LoRa datarate identifier
	Fdev *uint      `json:"fdev,omitempty"` // FSK frequency deviation (unsigned integer, in Hz)
	Freq *float64   `json:"freq,omitempty"` // RX Central frequency in MHx (unsigned float, Hz precision)
	Imme *bool      `json:"imme,omitempty"` // Send packet immediately (will ignore tmst & time)
	Ipol *bool      `json:"ipol,omitempty"` // Lora modulation polarization inversion
	Lsnr *float64   `json:"lsnr,omitempty"` // LoRa SNR ratio in dB (signed float, 0.1 dB precision)
	Modu *string    `json:"modu,omitempty"` // Modulation identifier "LORA" or "FSK"
	Ncrc *bool      `json:"ncrc,omitempty"` // If true, disable the CRC of the physical layer (optional)
	Powe *uint      `json:"powe,omitempty"` // TX output power in dBm (unsigned integer, dBm precision)
	Prea *uint      `json:"prea,omitempty"` // RF preamble size (unsigned integer)
	Rfch *uint      `json:"rfch,omitempty"` // Concentrator "RF chain" used for RX (unsigned integer)
	Rssi *int       `json:"rssi,omitempty"` // RSSI in dBm (signed integer, 1 dB precision)
	Size *uint      `json:"size,omitempty"` // RF packet payload size in bytes (unsigned integer)
	Stat *int       `json:"stat,omitempty"` // CRC status: 1 - OK, -1 = fail, 0 = no CRC
	Time *time.Time `json:"-"`              // UTC time of pkt RX, us precision, ISO 8601 'compact' format
	Tmst *uint      `json:"tmst,omitempty"` // Internal timestamp of "RX finished" event (32b unsigned)
}

type metadata Metadata

// MarshalJSON implements the json.Marshal interface
func (m Metadata) MarshalJSON() ([]byte, error) {
	var d *semtech.Datrparser
	var t *semtech.Timeparser

	if m.Datr != nil {
		d = new(semtech.Datrparser)
		if m.Modu != nil && *m.Modu == "FSK" {
			*d = semtech.Datrparser{Kind: "uint", Value: *m.Datr}
		} else {
			*d = semtech.Datrparser{Kind: "string", Value: *m.Datr}
		}
	}

	if m.Time != nil {
		t = new(semtech.Timeparser)
		*t = semtech.Timeparser{Layout: time.RFC3339Nano, Value: m.Time}
	}

	return json.Marshal(metadataProxy{
		metadata: metadata(m),
		Datr:     d,
		Time:     t,
	})
}

// UnmarshalJSON implements the json.Unmarshaler interface
func (m *Metadata) UnmarshalJSON(raw []byte) error {
	return nil
}

// type metadataProxy is used to conveniently marshal and unmarshal Metadata structure.
//
// Datr field could be either string or uint depending on the Modu field.
// Time field could be parsed in a lot of different way depending of the time format.
// This proxy make sure that everything is marshaled and unmarshaled to the right thing and allow
// the Metadata struct to be user-friendly.
type metadataProxy struct {
	metadata
	Datr *semtech.Datrparser `json:"datr,omitempty"`
	Time *semtech.Timeparser `json:"time,omitempty"`
}
<commit_msg>[broker] Implement Metadata.UnmarshalJSON()<commit_after>// Copyright © 2015 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package components

import (
	"encoding/json"
	"fmt"
	"github.com/thethingsnetwork/core/semtech"
	"time"
)

type Metadata struct {
	Chan *uint      `json:"chan,omitempty"` // Concentrator "IF" channel used for RX (unsigned integer)
	Codr *string    `json:"codr,omitempty"` // LoRa ECC coding rate identifier
	Data *string    `json:"data,omitempty"` // Base64 encoded RF packet payload, padded
	Datr *string    `json:"-"`              // FSK datarate (unsigned in bit per second) || LoRa datarate identifier
	Fdev *uint      `json:"fdev,omitempty"` // FSK frequency deviation (unsigned integer, in Hz)
	Freq *float64   `json:"freq,omitempty"` // RX Central frequency in MHx (unsigned float, Hz precision)
	Imme *bool      `json:"imme,omitempty"` // Send packet immediately (will ignore tmst & time)
	Ipol *bool      `json:"ipol,omitempty"` // Lora modulation polarization inversion
	Lsnr *float64   `json:"lsnr,omitempty"` // LoRa SNR ratio in dB (signed float, 0.1 dB precision)
	Modu *string    `json:"modu,omitempty"` // Modulation identifier "LORA" or "FSK"
	Ncrc *bool      `json:"ncrc,omitempty"` // If true, disable the CRC of the physical layer (optional)
	Powe *uint      `json:"powe,omitempty"` // TX output power in dBm (unsigned integer, dBm precision)
	Prea *uint      `json:"prea,omitempty"` // RF preamble size (unsigned integer)
	Rfch *uint      `json:"rfch,omitempty"` // Concentrator "RF chain" used for RX (unsigned integer)
	Rssi *int       `json:"rssi,omitempty"` // RSSI in dBm (signed integer, 1 dB precision)
	Size *uint      `json:"size,omitempty"` // RF packet payload size in bytes (unsigned integer)
	Stat *int       `json:"stat,omitempty"` // CRC status: 1 - OK, -1 = fail, 0 = no CRC
	Time *time.Time `json:"-"`              // UTC time of pkt RX, us precision, ISO 8601 'compact' format
	Tmst *uint      `json:"tmst,omitempty"` // Internal timestamp of "RX finished" event (32b unsigned)
}

type metadata Metadata

// MarshalJSON implements the json.Marshal interface
func (m Metadata) MarshalJSON() ([]byte, error) {
	var d *semtech.Datrparser
	var t *semtech.Timeparser

	if m.Datr != nil {
		d = new(semtech.Datrparser)
		if m.Modu != nil && *m.Modu == "FSK" {
			*d = semtech.Datrparser{Kind: "uint", Value: *m.Datr}
		} else {
			*d = semtech.Datrparser{Kind: "string", Value: *m.Datr}
		}
	}

	if m.Time != nil {
		t = new(semtech.Timeparser)
		*t = semtech.Timeparser{Layout: time.RFC3339Nano, Value: m.Time}
	}

	return json.Marshal(metadataProxy{
		metadata: metadata(m),
		Datr:     d,
		Time:     t,
	})
}

// UnmarshalJSON implements the json.Unmarshaler interface
func (m *Metadata) UnmarshalJSON(raw []byte) error {
	if m == nil {
		return fmt.Errorf("Cannot unmarshal nil Metadata")
	}

	proxy := metadataProxy{}
	if err := json.Unmarshal(raw, &proxy); err != nil {
		return err
	}
	*m = Metadata(proxy.metadata)
	if proxy.Time != nil {
		m.Time = proxy.Time.Value
	}

	if proxy.Datr != nil {
		m.Datr = &proxy.Datr.Value
	}

	return nil
}

// type metadataProxy is used to conveniently marshal and unmarshal Metadata structure.
//
// Datr field could be either string or uint depending on the Modu field.
// Time field could be parsed in a lot of different way depending of the time format.
// This proxy make sure that everything is marshaled and unmarshaled to the right thing and allow
// the Metadata struct to be user-friendly.
type metadataProxy struct {
	metadata
	Datr *semtech.Datrparser `json:"datr,omitempty"`
	Time *semtech.Timeparser `json:"time,omitempty"`
}
<|endoftext|>


<commit_before>package genhandler

import (
	"bytes"
	"strings"
	"text/template"

	pbdescriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
	"github.com/golang/protobuf/protoc-gen-go/generator"
	"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
	"github.com/pkg/errors"
)

var (
	errNoTargetService = errors.New("no target service defined in the file")
)

var pkg map[string]string

type param struct {
	*descriptor.File
	Imports          []descriptor.GoPackage
	SwaggerBuffer    []byte
	ApplyMiddlewares bool
	CurrentPath      string
}

func applyImplTemplate(p param) (string, error) {
	w := bytes.NewBuffer(nil)

	if err := implTemplate.Execute(w, p); err != nil {
		return "", err
	}

	return w.String(), nil
}

func applyDescTemplate(p param) (string, error) {
	// r := &http.Request{}
	// r.URL.Query()
	w := bytes.NewBuffer(nil)
	if err := headerTemplate.Execute(w, p); err != nil {
		return "", err
	}

	if err := regTemplate.ExecuteTemplate(w, "base", p); err != nil {
		return "", err
	}

	if err := clientTemplate.Execute(w, p); err != nil {
		return "", err
	}

	if err := marshalersTemplate.Execute(w, p); err != nil {
		return "", err
	}

	if err := patternsTemplate.ExecuteTemplate(w, "base", p); err != nil {
		return "", err
	}

	if p.SwaggerBuffer != nil {
		if err := footerTemplate.Execute(w, p); err != nil {
			return "", err
		}
	}

	return w.String(), nil
}

var (
	varNameReplacer = strings.NewReplacer(
		".", "_",
		"/", "_",
		"-", "_",
	)
	funcMap = template.FuncMap{
		"hasAsterisk": func(ss []string) bool {
			for _, s := range ss {
				if s == "*" {
					return true
				}
			}
			return false
		},
		"varName": func(s string) string { return varNameReplacer.Replace(s) },
		"goTypeName": func(s string) string {
			toks := strings.Split(s, ".")
			for pos := range toks {
				toks[pos] = generator.CamelCase(toks[pos])
			}
			return strings.Join(toks, ".")
		},
		"byteStr":         func(b []byte) string { return string(b) },
		"escapeBackTicks": func(s string) string { return strings.Replace(s, "`", "` + \"``\" + `", -1) },
		"toGoType":        func(t pbdescriptor.FieldDescriptorProto_Type) string { return primitiveTypeToGo(t) },
		// arrayToPathInterp replaces chi-style path to fmt.Sprint-style path.
		"arrayToPathInterp": func(tpl string) string {
			vv := strings.Split(tpl, "/")
			ret := []string{}
			for _, v := range vv {
				if strings.HasPrefix(v, "{") {
					ret = append(ret, "%v")
					continue
				}
				ret = append(ret, v)
			}
			return strings.Join(ret, "/")
		},
		// returns safe package prefix with dot(.) or empty string by imported package name or alias
		"pkg": func(name string) string {
			if p, ok := pkg[name]; ok && p != "" {
				return p + "."
			}
			return ""
		},
		"hasBindings": hasBindings,
	}

	headerTemplate = template.Must(template.New("header").Funcs(funcMap).Parse(`
// Code generated by protoc-gen-goclay
// source: {{ .GetName }}
// DO NOT EDIT!

/*
Package {{ .GoPkg.Name }} is a self-registering gRPC and JSON+Swagger service definition.

It conforms to the github.com/utrack/clay/v2/transport Service interface.
*/
package {{ .GoPkg.Name }}
import (
    {{ range $i := .Imports }}{{ if $i.Standard }}{{ $i | printf "%s\n" }}{{ end }}{{ end }}

    {{ range $i := .Imports }}{{ if not $i.Standard }}{{ $i | printf "%s\n" }}{{ end }}{{ end }}
)

// Update your shared lib or downgrade generator to v1 if there's an error
var _ = {{ pkg "transport" }}IsVersion2

var _ = {{ pkg "ioutil" }}Discard
var _ {{ pkg "chi" }}Router
var _ {{ pkg "runtime" }}Marshaler
var _ {{ pkg "bytes" }}Buffer
var _ {{ pkg "context" }}Context
var _ {{ pkg "fmt" }}Formatter
var _ {{ pkg "strings" }}Reader
var _ {{ pkg "errors" }}Frame
var _ {{ pkg "httpruntime" }}Marshaler
var _ {{ pkg "http" }}Handler
`))

	footerTemplate = template.Must(template.New("footer").Funcs(funcMap).Parse(`
    var _swaggerDef_{{ varName .GetName }} = []byte(` + "`" + `{{ escapeBackTicks (byteStr .SwaggerBuffer) }}` + `
` + "`)" + `
`))

	marshalersTemplate = template.Must(template.New("patterns").Funcs(funcMap).Parse(`
{{ range $svc := .Services }}
// patterns for {{ $svc.GetName }}
var (
{{ range $m := $svc.Methods }}
{{ range $b := $m.Bindings }}

    pattern_goclay_{{ $svc.GetName }}_{{ $m.GetName }}_{{ $b.Index }} = "{{ $b.PathTmpl.Template }}"

    pattern_goclay_{{ $svc.GetName }}_{{ $m.GetName }}_{{ $b.Index }}_builder = func(
        {{ range $p := $b.PathParams -}}
            {{ $p.Target.GetName }} {{ toGoType $p.Target.GetType }},
        {{ end -}}
    ) string {
        return {{ pkg "fmt" }}Sprintf("{{ arrayToPathInterp $b.PathTmpl.Template }}",{{ range $p := $b.PathParams }}{{ $p.Target.GetName }},{{ end }})
    }

    {{ if not (hasAsterisk $b.ExplicitParams) }}
        unmarshaler_goclay_{{ $svc.GetName }}_{{ $m.GetName }}_{{ $b.Index }}_boundParams = map[string]struct{}{
            {{ range $n := $b.ExplicitParams -}}
                "{{ $n }}": struct{}{},
            {{ end }}
        }
    {{ end }}
{{ end }}
{{ end }}
)
{{ end }}
`))

	patternsTemplate = template.Must(template.New("patterns").Funcs(funcMap).Parse(`
{{ define "base" }}
{{ range $svc := .Services }}
// marshalers for {{ $svc.GetName }}
var (
{{ range $m := $svc.Methods }}
{{ range $b := $m.Bindings }}

    unmarshaler_goclay_{{ $svc.GetName }}_{{ $m.GetName }}_{{ $b.Index }} = func(r *{{ pkg "http" }}Request) (*{{$m.RequestType.GoType $m.Service.File.GoPkg.Path }},error) {
	var req {{$m.RequestType.GoType $m.Service.File.GoPkg.Path }}

        {{ if not (hasAsterisk $b.ExplicitParams) }}
            for k,v := range r.URL.Query() {
                if _,ok := unmarshaler_goclay_{{ $svc.GetName }}_{{ $m.GetName }}_{{ $b.Index }}_boundParams[{{ pkg "strings" }}ToLower(k)];ok {
                    continue
                }
                if err := {{ pkg "errors" }}Wrap({{ pkg "runtime" }}PopulateFieldFromPath(&req, k, v[0]), "couldn't populate field from Path"); err != nil {
                    return nil, {{ pkg "httpruntime" }}TransformUnmarshalerError(err)
                }
            }
        {{ end }}
        {{- if $b.Body -}}
            {{- template "unmbody" . -}}
        {{- end -}}
        {{- if $b.PathParams -}}
            {{- template "unmpath" . -}}
        {{ end }}
        return &req, nil
    }
{{ end }}
{{ end }}
{{ end }}
)
{{ end }}
{{ define "unmbody" }}
    inbound,_ := {{ pkg "httpruntime" }}MarshalerForRequest(r)
    if err := {{ pkg "errors" }}Wrap(inbound.Unmarshal(r.Body,&{{.Body.AssignableExpr "req"}}),"couldn't read request JSON"); err != nil {
        return nil, {{ pkg "httpruntime" }}TransformUnmarshalerError(err)
    }
{{ end }}
{{ define "unmpath" }}
    rctx := {{ pkg "chi" }}RouteContext(r.Context())
    if rctx == nil {
        panic("Only chi router is supported for GETs atm")
    }
    for pos,k := range rctx.URLParams.Keys {
        if err := {{ pkg "errors" }}Wrap({{ pkg "runtime" }}PopulateFieldFromPath(&req, k, rctx.URLParams.Values[pos]), "couldn't populate field from Path"); err != nil {
            return nil, {{ pkg "httpruntime" }}TransformUnmarshalerError(err)
        }
    }
{{ end }}
`))

	implTemplate = template.Must(template.New("impl").Funcs(funcMap).Parse(`
// Code generated by protoc-gen-goclay, but your can (must) modify it.
// source: {{ .GetName }}

package  {{ .GoPkg.Name }}

import (
    {{ range $i := .Imports }}{{ if $i.Standard }}{{ $i | printf "%s\n" }}{{ end }}{{ end }}

    {{ range $i := .Imports }}{{ if not $i.Standard }}{{ $i | printf "%s\n" }}{{ end }}{{ end }}
)

{{ range $service := .Services }}

type {{ $service.GetName }}Implementation struct {}

func New{{ $service.GetName }}() *{{ $service.GetName }}Implementation {
    return &{{ $service.GetName }}Implementation{}
}

{{ range $method := $service.Methods }}
func (i *{{ $service.GetName }}Implementation) {{ $method.Name }}(ctx {{ pkg "context" }}Context, req *{{ $method.RequestType.GoType $.CurrentPath }}) (*{{ $method.ResponseType.GoType $.CurrentPath }}, error) {
    return nil, {{ pkg "errors" }}New("not implemented")
}
{{ end }}

// GetDescription is a simple alias to the ServiceDesc constructor.
// It makes it possible to register the service implementation @ the server.
func (i *{{ $service.GetName }}Implementation) GetDescription() {{ pkg "transport" }}ServiceDesc {
    return {{ pkg "desc" }}New{{ $service.GetName }}ServiceDesc(i)
}

{{ end }}
`))
)
<commit_msg>fixed goclay generation for multiple services in one protofile<commit_after>package genhandler

import (
	"bytes"
	"strings"
	"text/template"

	pbdescriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
	"github.com/golang/protobuf/protoc-gen-go/generator"
	"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
	"github.com/pkg/errors"
)

var (
	errNoTargetService = errors.New("no target service defined in the file")
)

var pkg map[string]string

type param struct {
	*descriptor.File
	Imports          []descriptor.GoPackage
	SwaggerBuffer    []byte
	ApplyMiddlewares bool
	CurrentPath      string
}

func applyImplTemplate(p param) (string, error) {
	w := bytes.NewBuffer(nil)

	if err := implTemplate.Execute(w, p); err != nil {
		return "", err
	}

	return w.String(), nil
}

func applyDescTemplate(p param) (string, error) {
	// r := &http.Request{}
	// r.URL.Query()
	w := bytes.NewBuffer(nil)
	if err := headerTemplate.Execute(w, p); err != nil {
		return "", err
	}

	if err := regTemplate.ExecuteTemplate(w, "base", p); err != nil {
		return "", err
	}

	if err := clientTemplate.Execute(w, p); err != nil {
		return "", err
	}

	if err := marshalersTemplate.Execute(w, p); err != nil {
		return "", err
	}

	if err := patternsTemplate.ExecuteTemplate(w, "base", p); err != nil {
		return "", err
	}

	if p.SwaggerBuffer != nil {
		if err := footerTemplate.Execute(w, p); err != nil {
			return "", err
		}
	}

	return w.String(), nil
}

var (
	varNameReplacer = strings.NewReplacer(
		".", "_",
		"/", "_",
		"-", "_",
	)
	funcMap = template.FuncMap{
		"hasAsterisk": func(ss []string) bool {
			for _, s := range ss {
				if s == "*" {
					return true
				}
			}
			return false
		},
		"varName": func(s string) string { return varNameReplacer.Replace(s) },
		"goTypeName": func(s string) string {
			toks := strings.Split(s, ".")
			for pos := range toks {
				toks[pos] = generator.CamelCase(toks[pos])
			}
			return strings.Join(toks, ".")
		},
		"byteStr":         func(b []byte) string { return string(b) },
		"escapeBackTicks": func(s string) string { return strings.Replace(s, "`", "` + \"``\" + `", -1) },
		"toGoType":        func(t pbdescriptor.FieldDescriptorProto_Type) string { return primitiveTypeToGo(t) },
		// arrayToPathInterp replaces chi-style path to fmt.Sprint-style path.
		"arrayToPathInterp": func(tpl string) string {
			vv := strings.Split(tpl, "/")
			ret := []string{}
			for _, v := range vv {
				if strings.HasPrefix(v, "{") {
					ret = append(ret, "%v")
					continue
				}
				ret = append(ret, v)
			}
			return strings.Join(ret, "/")
		},
		// returns safe package prefix with dot(.) or empty string by imported package name or alias
		"pkg": func(name string) string {
			if p, ok := pkg[name]; ok && p != "" {
				return p + "."
			}
			return ""
		},
		"hasBindings": hasBindings,
	}

	headerTemplate = template.Must(template.New("header").Funcs(funcMap).Parse(`
// Code generated by protoc-gen-goclay
// source: {{ .GetName }}
// DO NOT EDIT!

/*
Package {{ .GoPkg.Name }} is a self-registering gRPC and JSON+Swagger service definition.

It conforms to the github.com/utrack/clay/v2/transport Service interface.
*/
package {{ .GoPkg.Name }}
import (
    {{ range $i := .Imports }}{{ if $i.Standard }}{{ $i | printf "%s\n" }}{{ end }}{{ end }}

    {{ range $i := .Imports }}{{ if not $i.Standard }}{{ $i | printf "%s\n" }}{{ end }}{{ end }}
)

// Update your shared lib or downgrade generator to v1 if there's an error
var _ = {{ pkg "transport" }}IsVersion2

var _ = {{ pkg "ioutil" }}Discard
var _ {{ pkg "chi" }}Router
var _ {{ pkg "runtime" }}Marshaler
var _ {{ pkg "bytes" }}Buffer
var _ {{ pkg "context" }}Context
var _ {{ pkg "fmt" }}Formatter
var _ {{ pkg "strings" }}Reader
var _ {{ pkg "errors" }}Frame
var _ {{ pkg "httpruntime" }}Marshaler
var _ {{ pkg "http" }}Handler
`))

	footerTemplate = template.Must(template.New("footer").Funcs(funcMap).Parse(`
    var _swaggerDef_{{ varName .GetName }} = []byte(` + "`" + `{{ escapeBackTicks (byteStr .SwaggerBuffer) }}` + `
` + "`)" + `
`))

	marshalersTemplate = template.Must(template.New("patterns").Funcs(funcMap).Parse(`
{{ range $svc := .Services }}
// patterns for {{ $svc.GetName }}
var (
{{ range $m := $svc.Methods }}
{{ range $b := $m.Bindings }}

    pattern_goclay_{{ $svc.GetName }}_{{ $m.GetName }}_{{ $b.Index }} = "{{ $b.PathTmpl.Template }}"

    pattern_goclay_{{ $svc.GetName }}_{{ $m.GetName }}_{{ $b.Index }}_builder = func(
        {{ range $p := $b.PathParams -}}
            {{ $p.Target.GetName }} {{ toGoType $p.Target.GetType }},
        {{ end -}}
    ) string {
        return {{ pkg "fmt" }}Sprintf("{{ arrayToPathInterp $b.PathTmpl.Template }}",{{ range $p := $b.PathParams }}{{ $p.Target.GetName }},{{ end }})
    }

    {{ if not (hasAsterisk $b.ExplicitParams) }}
        unmarshaler_goclay_{{ $svc.GetName }}_{{ $m.GetName }}_{{ $b.Index }}_boundParams = map[string]struct{}{
            {{ range $n := $b.ExplicitParams -}}
                "{{ $n }}": struct{}{},
            {{ end }}
        }
    {{ end }}
{{ end }}
{{ end }}
)
{{ end }}
`))

	patternsTemplate = template.Must(template.New("patterns").Funcs(funcMap).Parse(`
{{ define "base" }}
{{ range $svc := .Services }}
// marshalers for {{ $svc.GetName }}
var (
{{ range $m := $svc.Methods }}
{{ range $b := $m.Bindings }}

    unmarshaler_goclay_{{ $svc.GetName }}_{{ $m.GetName }}_{{ $b.Index }} = func(r *{{ pkg "http" }}Request) (*{{$m.RequestType.GoType $m.Service.File.GoPkg.Path }},error) {
	var req {{$m.RequestType.GoType $m.Service.File.GoPkg.Path }}

        {{ if not (hasAsterisk $b.ExplicitParams) }}
            for k,v := range r.URL.Query() {
                if _,ok := unmarshaler_goclay_{{ $svc.GetName }}_{{ $m.GetName }}_{{ $b.Index }}_boundParams[{{ pkg "strings" }}ToLower(k)];ok {
                    continue
                }
                if err := {{ pkg "errors" }}Wrap({{ pkg "runtime" }}PopulateFieldFromPath(&req, k, v[0]), "couldn't populate field from Path"); err != nil {
                    return nil, {{ pkg "httpruntime" }}TransformUnmarshalerError(err)
                }
            }
        {{ end }}
        {{- if $b.Body -}}
            {{- template "unmbody" . -}}
        {{- end -}}
        {{- if $b.PathParams -}}
            {{- template "unmpath" . -}}
        {{ end }}
        return &req, nil
    }
{{ end }}
{{ end }}
)
{{ end }}
{{ end }}
{{ define "unmbody" }}
    inbound,_ := {{ pkg "httpruntime" }}MarshalerForRequest(r)
    if err := {{ pkg "errors" }}Wrap(inbound.Unmarshal(r.Body,&{{.Body.AssignableExpr "req"}}),"couldn't read request JSON"); err != nil {
        return nil, {{ pkg "httpruntime" }}TransformUnmarshalerError(err)
    }
{{ end }}
{{ define "unmpath" }}
    rctx := {{ pkg "chi" }}RouteContext(r.Context())
    if rctx == nil {
        panic("Only chi router is supported for GETs atm")
    }
    for pos,k := range rctx.URLParams.Keys {
        if err := {{ pkg "errors" }}Wrap({{ pkg "runtime" }}PopulateFieldFromPath(&req, k, rctx.URLParams.Values[pos]), "couldn't populate field from Path"); err != nil {
            return nil, {{ pkg "httpruntime" }}TransformUnmarshalerError(err)
        }
    }
{{ end }}
`))

	implTemplate = template.Must(template.New("impl").Funcs(funcMap).Parse(`
// Code generated by protoc-gen-goclay, but your can (must) modify it.
// source: {{ .GetName }}

package  {{ .GoPkg.Name }}

import (
    {{ range $i := .Imports }}{{ if $i.Standard }}{{ $i | printf "%s\n" }}{{ end }}{{ end }}

    {{ range $i := .Imports }}{{ if not $i.Standard }}{{ $i | printf "%s\n" }}{{ end }}{{ end }}
)

{{ range $service := .Services }}

type {{ $service.GetName }}Implementation struct {}

func New{{ $service.GetName }}() *{{ $service.GetName }}Implementation {
    return &{{ $service.GetName }}Implementation{}
}

{{ range $method := $service.Methods }}
func (i *{{ $service.GetName }}Implementation) {{ $method.Name }}(ctx {{ pkg "context" }}Context, req *{{ $method.RequestType.GoType $.CurrentPath }}) (*{{ $method.ResponseType.GoType $.CurrentPath }}, error) {
    return nil, {{ pkg "errors" }}New("not implemented")
}
{{ end }}

// GetDescription is a simple alias to the ServiceDesc constructor.
// It makes it possible to register the service implementation @ the server.
func (i *{{ $service.GetName }}Implementation) GetDescription() {{ pkg "transport" }}ServiceDesc {
    return {{ pkg "desc" }}New{{ $service.GetName }}ServiceDesc(i)
}

{{ end }}
`))
)
<|endoftext|>


<commit_before>/*
Copyright 2020 The Kubernetes Authors.

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

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

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

package server

import (
	"net"
	"net/http"
	"strings"
	"sync"
	goatomic "sync/atomic"

	"go.uber.org/atomic"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/klog/v2"
	netutils "k8s.io/utils/net"
)

// terminationLoggingListener wraps the given listener to mark late connections
// as such, identified by the remote address. In parallel, we have a filter that
// logs bad requests through these connections. We need this filter to get
// access to the http path in order to filter out healthz or readyz probes that
// are allowed at any point during termination.
//
// Connections are late after the lateStopCh has been closed.
type terminationLoggingListener struct {
	net.Listener
	lateStopCh <-chan struct{}
}

type eventfFunc func(eventType, reason, messageFmt string, args ...interface{})

var (
	lateConnectionRemoteAddrsLock sync.RWMutex
	lateConnectionRemoteAddrs     = map[string]bool{}

	unexpectedRequestsEventf goatomic.Value
)

func (l *terminationLoggingListener) Accept() (net.Conn, error) {
	c, err := l.Listener.Accept()
	if err != nil {
		return nil, err
	}

	select {
	case <-l.lateStopCh:
		lateConnectionRemoteAddrsLock.Lock()
		defer lateConnectionRemoteAddrsLock.Unlock()
		lateConnectionRemoteAddrs[c.RemoteAddr().String()] = true
	default:
	}

	return c, nil
}

// WithLateConnectionFilter logs every non-probe request that comes through a late connection identified by remote address.
func WithLateConnectionFilter(handler http.Handler) http.Handler {
	var lateRequestReceived atomic.Bool

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		lateConnectionRemoteAddrsLock.RLock()
		late := lateConnectionRemoteAddrs[r.RemoteAddr]
		lateConnectionRemoteAddrsLock.RUnlock()

		if late {
			if pth := "/" + strings.TrimLeft(r.URL.Path, "/"); pth != "/readyz" && pth != "/healthz" && pth != "/livez" {
				if isLocal(r) {
					klog.V(4).Infof("Loopback request to %q (user agent %q) through connection created very late in the graceful termination process (more than 80%% has passed). This client probably does not watch /readyz and might get failures when termination is over.", r.URL.Path, r.UserAgent())
				} else {
					klog.Warningf("Request to %q (source IP %s, user agent %q) through a connection created very late in the graceful termination process (more than 80%% has passed), possibly a sign for a broken load balancer setup.", r.URL.Path, r.RemoteAddr, r.UserAgent())

					// create only one event to avoid event spam.
					var eventf eventfFunc
					eventf, _ = unexpectedRequestsEventf.Load().(eventfFunc)
					if swapped := lateRequestReceived.CAS(false, true); swapped && eventf != nil {
						eventf(corev1.EventTypeWarning, "LateConnections", "The apiserver received connections (e.g. from %q, user agent %q) very late in the graceful termination process, possibly a sign for a broken load balancer setup.", r.RemoteAddr, r.UserAgent())
					}
				}
			}
		}

		handler.ServeHTTP(w, r)
	})
}

// WithNonReadyRequestLogging rejects the request until the process has been ready once.
func WithNonReadyRequestLogging(handler http.Handler, hasBeenReadySignal lifecycleSignal) http.Handler {
	if hasBeenReadySignal == nil {
		return handler
	}

	var nonReadyRequestReceived atomic.Bool

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		select {
		case <-hasBeenReadySignal.Signaled():
			handler.ServeHTTP(w, r)
			return
		default:
		}

		// ignore connections to local IP. Those clients better know what they are doing.
		if pth := "/" + strings.TrimLeft(r.URL.Path, "/"); pth != "/readyz" && pth != "/healthz" && pth != "/livez" {
			if isLocal(r) {
				if !isKubeApiserverLoopBack(r) {
					klog.V(2).Infof("Loopback request to %q (user agent %q) before server is ready. This client probably does not watch /readyz and might get inconsistent answers.", r.URL.Path, r.UserAgent())
				}
			} else {
				klog.Warningf("Request to %q (source IP %s, user agent %q) before server is ready, possibly a sign for a broken load balancer setup.", r.URL.Path, r.RemoteAddr, r.UserAgent())

				// create only one event to avoid event spam.
				var eventf eventfFunc
				eventf, _ = unexpectedRequestsEventf.Load().(eventfFunc)
				if swapped := nonReadyRequestReceived.CAS(false, true); swapped && eventf != nil {
					eventf(corev1.EventTypeWarning, "NonReadyRequests", "The kube-apiserver received requests (e.g. from %q, user agent %q, accessing %s) before it was ready, possibly a sign for a broken load balancer setup.", r.RemoteAddr, r.UserAgent(), r.URL.Path)
				}
			}
		}

		handler.ServeHTTP(w, r)
	})
}

func isLocal(req *http.Request) bool {
	host, _, err := net.SplitHostPort(req.RemoteAddr)
	if err != nil {
		// ignore error and keep going
	} else if ip := netutils.ParseIPSloppy(host); ip != nil {
		return ip.IsLoopback()
	}

	return false
}

func isKubeApiserverLoopBack(req *http.Request) bool {
	return strings.HasPrefix(req.UserAgent(), "kube-apiserver/")
}
<commit_msg>UPSTREAM: <carry>: annotate audit events for requests during unready phase and graceful termination phase<commit_after>/*
Copyright 2020 The Kubernetes Authors.

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

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

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

package server

import (
	"fmt"
	"net"
	"net/http"
	"strings"
	"sync"
	goatomic "sync/atomic"

	"go.uber.org/atomic"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/apiserver/pkg/audit"
	"k8s.io/klog/v2"
	netutils "k8s.io/utils/net"
)

// terminationLoggingListener wraps the given listener to mark late connections
// as such, identified by the remote address. In parallel, we have a filter that
// logs bad requests through these connections. We need this filter to get
// access to the http path in order to filter out healthz or readyz probes that
// are allowed at any point during termination.
//
// Connections are late after the lateStopCh has been closed.
type terminationLoggingListener struct {
	net.Listener
	lateStopCh <-chan struct{}
}

type eventfFunc func(eventType, reason, messageFmt string, args ...interface{})

var (
	lateConnectionRemoteAddrsLock sync.RWMutex
	lateConnectionRemoteAddrs     = map[string]bool{}

	unexpectedRequestsEventf goatomic.Value
)

func (l *terminationLoggingListener) Accept() (net.Conn, error) {
	c, err := l.Listener.Accept()
	if err != nil {
		return nil, err
	}

	select {
	case <-l.lateStopCh:
		lateConnectionRemoteAddrsLock.Lock()
		defer lateConnectionRemoteAddrsLock.Unlock()
		lateConnectionRemoteAddrs[c.RemoteAddr().String()] = true
	default:
	}

	return c, nil
}

// WithLateConnectionFilter logs every non-probe request that comes through a late connection identified by remote address.
func WithLateConnectionFilter(handler http.Handler) http.Handler {
	var lateRequestReceived atomic.Bool

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		lateConnectionRemoteAddrsLock.RLock()
		late := lateConnectionRemoteAddrs[r.RemoteAddr]
		lateConnectionRemoteAddrsLock.RUnlock()

		if late {
			if pth := "/" + strings.TrimLeft(r.URL.Path, "/"); pth != "/readyz" && pth != "/healthz" && pth != "/livez" {
				if isLocal(r) {
					audit.AddAuditAnnotation(r.Context(), "openshift.io/during-graceful", fmt.Sprintf("loopback=true,%v,readyz=false", r.URL.Host))
					klog.V(4).Infof("Loopback request to %q (user agent %q) through connection created very late in the graceful termination process (more than 80%% has passed). This client probably does not watch /readyz and might get failures when termination is over.", r.URL.Path, r.UserAgent())
				} else {
					audit.AddAuditAnnotation(r.Context(), "openshift.io/during-graceful", fmt.Sprintf("loopback=false,%v,readyz=false", r.URL.Host))
					klog.Warningf("Request to %q (source IP %s, user agent %q) through a connection created very late in the graceful termination process (more than 80%% has passed), possibly a sign for a broken load balancer setup.", r.URL.Path, r.RemoteAddr, r.UserAgent())

					// create only one event to avoid event spam.
					var eventf eventfFunc
					eventf, _ = unexpectedRequestsEventf.Load().(eventfFunc)
					if swapped := lateRequestReceived.CAS(false, true); swapped && eventf != nil {
						eventf(corev1.EventTypeWarning, "LateConnections", "The apiserver received connections (e.g. from %q, user agent %q) very late in the graceful termination process, possibly a sign for a broken load balancer setup.", r.RemoteAddr, r.UserAgent())
					}
				}
			}
		}

		handler.ServeHTTP(w, r)
	})
}

// WithNonReadyRequestLogging rejects the request until the process has been ready once.
func WithNonReadyRequestLogging(handler http.Handler, hasBeenReadySignal lifecycleSignal) http.Handler {
	if hasBeenReadySignal == nil {
		return handler
	}

	var nonReadyRequestReceived atomic.Bool

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		select {
		case <-hasBeenReadySignal.Signaled():
			handler.ServeHTTP(w, r)
			return
		default:
		}

		// ignore connections to local IP. Those clients better know what they are doing.
		if pth := "/" + strings.TrimLeft(r.URL.Path, "/"); pth != "/readyz" && pth != "/healthz" && pth != "/livez" {
			if isLocal(r) {
				if !isKubeApiserverLoopBack(r) {
					audit.AddAuditAnnotation(r.Context(), "openshift.io/unready", fmt.Sprintf("loopback=true,%v,readyz=false", r.URL.Host))
					klog.V(2).Infof("Loopback request to %q (user agent %q) before server is ready. This client probably does not watch /readyz and might get inconsistent answers.", r.URL.Path, r.UserAgent())
				}
			} else {
				audit.AddAuditAnnotation(r.Context(), "openshift.io/unready", fmt.Sprintf("loopback=false,%v,readyz=false", r.URL.Host))
				klog.Warningf("Request to %q (source IP %s, user agent %q) before server is ready, possibly a sign for a broken load balancer setup.", r.URL.Path, r.RemoteAddr, r.UserAgent())

				// create only one event to avoid event spam.
				var eventf eventfFunc
				eventf, _ = unexpectedRequestsEventf.Load().(eventfFunc)
				if swapped := nonReadyRequestReceived.CAS(false, true); swapped && eventf != nil {
					eventf(corev1.EventTypeWarning, "NonReadyRequests", "The kube-apiserver received requests (e.g. from %q, user agent %q, accessing %s) before it was ready, possibly a sign for a broken load balancer setup.", r.RemoteAddr, r.UserAgent(), r.URL.Path)
				}
			}
		}

		handler.ServeHTTP(w, r)
	})
}

func isLocal(req *http.Request) bool {
	host, _, err := net.SplitHostPort(req.RemoteAddr)
	if err != nil {
		// ignore error and keep going
	} else if ip := netutils.ParseIPSloppy(host); ip != nil {
		return ip.IsLoopback()
	}

	return false
}

func isKubeApiserverLoopBack(req *http.Request) bool {
	return strings.HasPrefix(req.UserAgent(), "kube-apiserver/")
}
<|endoftext|>


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

// +build windows

package declarative

import (
	"github.com/lxn/walk"
)

type MainWindow struct {
	AssignTo         **walk.MainWindow
	Name             string
	Enabled          Property
	Visible          Property
	Font             Font
	MinSize          Size
	MaxSize          Size
	ContextMenuItems []MenuItem
	OnKeyDown        walk.KeyEventHandler
	OnKeyPress       walk.KeyEventHandler
	OnKeyUp          walk.KeyEventHandler
	OnMouseDown      walk.MouseEventHandler
	OnMouseMove      walk.MouseEventHandler
	OnMouseUp        walk.MouseEventHandler
	OnDropFiles      walk.DropFilesEventHandler
	OnSizeChanged    walk.EventHandler
	Title            string
	Size             Size
	DataBinder       DataBinder
	Layout           Layout
	Children         []Widget
	MenuItems        []MenuItem
	ToolBarItems     []MenuItem // Deprecated, use ToolBar instead
	ToolBar          ToolBar
}

func (mw MainWindow) Create() error {
	w, err := walk.NewMainWindow()
	if err != nil {
		return err
	}

	tlwi := topLevelWindowInfo{
		Name:             mw.Name,
		Font:             mw.Font,
		ToolTipText:      "",
		MinSize:          mw.MinSize,
		MaxSize:          mw.MaxSize,
		ContextMenuItems: mw.ContextMenuItems,
		OnKeyDown:        mw.OnKeyDown,
		OnKeyPress:       mw.OnKeyPress,
		OnKeyUp:          mw.OnKeyUp,
		OnMouseDown:      mw.OnMouseDown,
		OnMouseMove:      mw.OnMouseMove,
		OnMouseUp:        mw.OnMouseUp,
		OnSizeChanged:    mw.OnSizeChanged,
		DataBinder:       mw.DataBinder,
		Layout:           mw.Layout,
		Children:         mw.Children,
	}

	builder := NewBuilder(nil)

	w.SetSuspended(true)
	builder.Defer(func() error {
		w.SetSuspended(false)
		return nil
	})

	builder.deferBuildMenuActions(w.Menu(), mw.MenuItems)

	return builder.InitWidget(tlwi, w, func() error {
		if len(mw.ToolBar.Items) > 0 {
			var tb *walk.ToolBar
			if mw.ToolBar.AssignTo == nil {
				mw.ToolBar.AssignTo = &tb
			}

			if err := mw.ToolBar.Create(builder); err != nil {
				return err
			}

			old := w.ToolBar()
			w.SetToolBar(*mw.ToolBar.AssignTo)
			old.Dispose()
		} else {
			builder.deferBuildActions(w.ToolBar().Actions(), mw.ToolBarItems)
		}

		if err := w.SetTitle(mw.Title); err != nil {
			return err
		}

		if err := w.SetSize(mw.Size.toW()); err != nil {
			return err
		}

		imageList, err := walk.NewImageList(walk.Size{16, 16}, 0)
		if err != nil {
			return err
		}
		w.ToolBar().SetImageList(imageList)

		if mw.OnDropFiles != nil {
			w.DropFiles().Attach(mw.OnDropFiles)
		}

		if mw.AssignTo != nil {
			*mw.AssignTo = w
		}

		builder.Defer(func() error {
			w.Show()

			return nil
		})

		return nil
	})
}

func (mw MainWindow) Run() (int, error) {
	var w *walk.MainWindow

	if mw.AssignTo == nil {
		mw.AssignTo = &w
	}

	if err := mw.Create(); err != nil {
		return 0, err
	}

	return (*mw.AssignTo).Run(), nil
}
<commit_msg>declarative/MainWindow: Don't show window if Visible == false, fixes #202<commit_after>// Copyright 2012 The Walk Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build windows

package declarative

import (
	"github.com/lxn/walk"
)

type MainWindow struct {
	AssignTo         **walk.MainWindow
	Name             string
	Enabled          Property
	Visible          Property
	Font             Font
	MinSize          Size
	MaxSize          Size
	ContextMenuItems []MenuItem
	OnKeyDown        walk.KeyEventHandler
	OnKeyPress       walk.KeyEventHandler
	OnKeyUp          walk.KeyEventHandler
	OnMouseDown      walk.MouseEventHandler
	OnMouseMove      walk.MouseEventHandler
	OnMouseUp        walk.MouseEventHandler
	OnDropFiles      walk.DropFilesEventHandler
	OnSizeChanged    walk.EventHandler
	Title            string
	Size             Size
	DataBinder       DataBinder
	Layout           Layout
	Children         []Widget
	MenuItems        []MenuItem
	ToolBarItems     []MenuItem // Deprecated, use ToolBar instead
	ToolBar          ToolBar
}

func (mw MainWindow) Create() error {
	w, err := walk.NewMainWindow()
	if err != nil {
		return err
	}

	tlwi := topLevelWindowInfo{
		Name:             mw.Name,
		Font:             mw.Font,
		ToolTipText:      "",
		MinSize:          mw.MinSize,
		MaxSize:          mw.MaxSize,
		ContextMenuItems: mw.ContextMenuItems,
		OnKeyDown:        mw.OnKeyDown,
		OnKeyPress:       mw.OnKeyPress,
		OnKeyUp:          mw.OnKeyUp,
		OnMouseDown:      mw.OnMouseDown,
		OnMouseMove:      mw.OnMouseMove,
		OnMouseUp:        mw.OnMouseUp,
		OnSizeChanged:    mw.OnSizeChanged,
		DataBinder:       mw.DataBinder,
		Layout:           mw.Layout,
		Children:         mw.Children,
	}

	builder := NewBuilder(nil)

	w.SetSuspended(true)
	builder.Defer(func() error {
		w.SetSuspended(false)
		return nil
	})

	builder.deferBuildMenuActions(w.Menu(), mw.MenuItems)

	return builder.InitWidget(tlwi, w, func() error {
		if len(mw.ToolBar.Items) > 0 {
			var tb *walk.ToolBar
			if mw.ToolBar.AssignTo == nil {
				mw.ToolBar.AssignTo = &tb
			}

			if err := mw.ToolBar.Create(builder); err != nil {
				return err
			}

			old := w.ToolBar()
			w.SetToolBar(*mw.ToolBar.AssignTo)
			old.Dispose()
		} else {
			builder.deferBuildActions(w.ToolBar().Actions(), mw.ToolBarItems)
		}

		if err := w.SetTitle(mw.Title); err != nil {
			return err
		}

		if err := w.SetSize(mw.Size.toW()); err != nil {
			return err
		}

		imageList, err := walk.NewImageList(walk.Size{16, 16}, 0)
		if err != nil {
			return err
		}
		w.ToolBar().SetImageList(imageList)

		if mw.OnDropFiles != nil {
			w.DropFiles().Attach(mw.OnDropFiles)
		}

		if mw.AssignTo != nil {
			*mw.AssignTo = w
		}

		builder.Defer(func() error {
			if mw.Visible != false {
				w.Show()
			}

			return nil
		})

		return nil
	})
}

func (mw MainWindow) Run() (int, error) {
	var w *walk.MainWindow

	if mw.AssignTo == nil {
		mw.AssignTo = &w
	}

	if err := mw.Create(); err != nil {
		return 0, err
	}

	return (*mw.AssignTo).Run(), nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/howeyc/fsnotify"
	"net/http"
	"os"
	"os/exec"
	"strings"
	"time"
)

func main() {
	watcher, _ := fsnotify.NewWatcher()
	defer watcher.Close()

	go reactToChanges(watcher)

	working, _ := os.Getwd()
	watcher.Watch(working)

	http.HandleFunc("/", homeHandler)
	http.HandleFunc("/latest", reportHandler)
	http.ListenAndServe(":8080", nil)
}

func reactToChanges(watcher *fsnotify.Watcher) {
	busy := false
	done := make(chan bool)
	ready := make(chan bool)

	for {
		select {
		case ev := <-watcher.Event:
			if strings.HasSuffix(ev.Name, ".go") && !busy {
				busy = true
				go test(done)
			}

		case err := <-watcher.Error:
			fmt.Println(err)

		case <-done:
			// TODO: rethink this delay?
			time.AfterFunc(1500*time.Millisecond, func() {
				ready <- true
			})

		case <-ready:
			busy = false
		}
	}
}

func test(done chan bool) {
	fmt.Println("Running tests...")

	// TODO: recurse into subdirectories and run tests...
	// oh yeah, and always check for new packages sprouting up,
	// or existing ones being removed...
	output, _ := exec.Command("go", "test", "-json").Output()
	result := parsePackageResult(string(output))

	serialized, _ := json.Marshal(result)
	// var buffer bytes.Buffer
	// json.Indent(&buffer, serialized, "", "  ")

	latest = string(serialized)
	done <- true
}

func homeHandler(writer http.ResponseWriter, request *http.Request) {
	fmt.Fprint(writer, "...") // TODO
}

func reportHandler(writer http.ResponseWriter, request *http.Request) {
	writer.Header().Set("Content-Type", "application/json")
	fmt.Fprint(writer, latest)
}

var latest string
<commit_msg>Server can update the watched directory.<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/howeyc/fsnotify"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

func main() {
	watcher, _ = fsnotify.NewWatcher() // TODO: err
	defer watcher.Close()

	fmt.Println("Initialized watcher...")

	go reactToChanges()

	working, _ := os.Getwd() // TODO: err
	updateWatch(working)

	http.HandleFunc("/", homeHandler)
	http.HandleFunc("/latest", reportHandler)
	http.HandleFunc("/watch", watchHandler)
	http.ListenAndServe(":8080", nil) // TODO: flag for port
}

func updateWatch(root string) {
	addNewWatches(root)
	removeOldWatches()
}
func addNewWatches(root string) {
	if rootWatch != root {
		adjustRoot(root)
	}

	watchNestedPaths(root)
}
func adjustRoot(root string) {
	fmt.Println("Watching new root:", root)
	for path, _ := range watched {
		removeWatch(path)
	}
	rootWatch = root
	watch(root)
}
func watchNestedPaths(root string) {
	filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
		if matches, _ := filepath.Glob(filepath.Join(path, "*test.go")); len(matches) > 0 {
			watch(path)
		}
		return nil
	})
}
func watch(path string) {
	if !watching(path) {
		fmt.Println("Watching:", path)
		watched[path] = true
		watcher.Watch(path)
	}
}

func watching(path string) bool {
	for w, _ := range watched {
		if w == path {
			return true
		}
	}
	return false
}

func removeOldWatches() {
	for path, _ := range watched {
		if !exists(path) {
			removeWatch(path)
		}
	}
}
func removeWatch(path string) {
	delete(watched, path)
	watcher.RemoveWatch(path)
	fmt.Println("No longer watching:", path)
}

func exists(path string) bool {
	info, err := os.Stat(path)
	return err == nil && info.IsDir()
}

func reactToChanges() {
	busy := false
	done := make(chan bool)
	ready := make(chan bool)

	for {
		select {
		case ev := <-watcher.Event:
			updateWatch(rootWatch)
			if strings.HasSuffix(ev.Name, ".go") && !busy {
				busy = true
				go runTests(done)
			}

		case err := <-watcher.Error:
			fmt.Println(err)

		case <-done:
			// TODO: rethink this delay?
			time.AfterFunc(1500*time.Millisecond, func() {
				ready <- true
			})

		case <-ready:
			busy = false
		}
	}
}

func runTests(done chan bool) {
	results := []PackageResult{}
	for path, _ := range watched {
		fmt.Println("Running tests at:", path)
		os.Chdir(path)                                            // TODO: err
		output, _ := exec.Command("go", "test", "-json").Output() // TODO: err
		result := parsePackageResult(string(output))
		fmt.Println("Result: ", result.Passed)
		results = append(results, result)
	}
	serialized, _ := json.Marshal(results) // TODO: err
	latestOutput = string(serialized)
	done <- true
}

func homeHandler(writer http.ResponseWriter, request *http.Request) {
	fmt.Fprint(writer, "...") // TODO: setup static handler for html and javascript?
}

func reportHandler(writer http.ResponseWriter, request *http.Request) {
	writer.Header().Set("Content-Type", "application/json")
	fmt.Fprint(writer, latestOutput)
}

func watchHandler(writer http.ResponseWriter, request *http.Request) {
	if request.Method == "GET" {
		writer.Write([]byte(rootWatch))
		return
	}

	value := request.URL.Query()["root"]
	if len(value) == 0 {
		http.Error(writer, "No 'root' query string parameter included!", http.StatusBadRequest)
		return
	}
	newRoot := value[0]
	if !exists(newRoot) {
		http.Error(writer, "The 'root' value provided is not an existing directory.", http.StatusNotFound)
	} else {
		updateWatch(newRoot)
	}
}

var (
	latestOutput string
	rootWatch    string
	watched      map[string]bool
	watcher      *fsnotify.Watcher
)
<|endoftext|>