<commit_before>package debuganimations

import (
	"errors"
	"github.com/jkomoros/boardgame"
)

//+autoreader readsetter
type moveMoveCardBetweenShortStacks struct {
	boardgame.DefaultMove
	FromFirst bool
}

/**************************************************
 *
 * moveMoveCardBetweenShortStacks Implementation
 *
 **************************************************/

func MoveMoveCardBetweenShortStacksFactory(state boardgame.State) boardgame.Move {
	result := &moveMoveCardBetweenShortStacks{
		boardgame.DefaultMove{
			"Move Card Between Short Stacks",
			"Moves a card between two short stacks",
		},
		true,
	}

	if state != nil {
		gameState, _ := concreteStates(state)

		if gameState.FirstShortStack.NumComponents() < 1 {
			result.FromFirst = false
		}
	}

	return result
}

func (m *moveMoveCardBetweenShortStacks) Legal(state boardgame.State, proposer boardgame.PlayerIndex) error {

	game, _ := concreteStates(state)

	if game.FirstShortStack.NumComponents() < 1 && m.FromFirst {
		return errors.New("First short stack has no cards to move")
	}

	if game.SecondShortStack.NumComponents() < 1 && !m.FromFirst {
		return errors.New("Second short stack has no cards to move")
	}

	return nil
}

func (m *moveMoveCardBetweenShortStacks) Apply(state boardgame.MutableState) error {

	game, _ := concreteStates(state)

	from := game.SecondShortStack
	to := game.FirstShortStack

	if m.FromFirst {
		from = game.FirstShortStack
		to = game.SecondShortStack
	}

	if err := from.MoveComponent(boardgame.FirstComponentIndex, to, boardgame.LastSlotIndex); err != nil {
		return err
	}

	return nil
}
<commit_msg>The cards in debuganimations move to top of stack. Part of #348.<commit_after>package debuganimations

import (
	"errors"
	"github.com/jkomoros/boardgame"
)

//+autoreader readsetter
type moveMoveCardBetweenShortStacks struct {
	boardgame.DefaultMove
	FromFirst bool
}

/**************************************************
 *
 * moveMoveCardBetweenShortStacks Implementation
 *
 **************************************************/

func MoveMoveCardBetweenShortStacksFactory(state boardgame.State) boardgame.Move {
	result := &moveMoveCardBetweenShortStacks{
		boardgame.DefaultMove{
			"Move Card Between Short Stacks",
			"Moves a card between two short stacks",
		},
		true,
	}

	if state != nil {
		gameState, _ := concreteStates(state)

		if gameState.FirstShortStack.NumComponents() < 1 {
			result.FromFirst = false
		}
	}

	return result
}

func (m *moveMoveCardBetweenShortStacks) Legal(state boardgame.State, proposer boardgame.PlayerIndex) error {

	game, _ := concreteStates(state)

	if game.FirstShortStack.NumComponents() < 1 && m.FromFirst {
		return errors.New("First short stack has no cards to move")
	}

	if game.SecondShortStack.NumComponents() < 1 && !m.FromFirst {
		return errors.New("Second short stack has no cards to move")
	}

	return nil
}

func (m *moveMoveCardBetweenShortStacks) Apply(state boardgame.MutableState) error {

	game, _ := concreteStates(state)

	from := game.SecondShortStack
	to := game.FirstShortStack

	if m.FromFirst {
		from = game.FirstShortStack
		to = game.SecondShortStack
	}

	if err := from.MoveComponent(boardgame.FirstComponentIndex, to, boardgame.FirstSlotIndex); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package slackapi

import (
	"encoding/json"
	"testing"
)

func CheckResponse(t *testing.T, x interface{}, y string) {
	out, err := json.Marshal(x)
	if err != nil {
		t.Fatal("json fromat;", err)
	}
	if string(out) != y {
		t.Fatalf("invalid json response;\n- %s\n+ %s\n", y, out)
	}
}

func TestAPITest(t *testing.T) {
	s := New()
	x := s.APITest()
	y := `{"ok":true}`
	CheckResponse(t, x, y)
}

func TestAppsList(t *testing.T) {
	s := New()
	x := s.AppsList()
	y := `{"ok":false,"error":"not_authed","apps":null,"cache_ts":""}`
	CheckResponse(t, x, y)
}

func TestAuthRevoke(t *testing.T) {
	s := New()
	x := s.AuthRevoke()
	y := `{"ok":false,"error":"not_authed","revoked":false}`
	CheckResponse(t, x, y)
}

func TestAuthTest(t *testing.T) {
	s := New()
	x, err := s.AuthTest()
	if err != nil {
		t.Fatal(err)
	}
	y := `{"ok":false,"error":"not_authed","team":"","team_id":"","url":"","user":"","user_id":""}`
	CheckResponse(t, x, y)
}

func TestBotsInfo(t *testing.T) {
	s := New()
	x := s.BotsInfo("user")
	y := `{"ok":false,"error":"not_authed","bot":{"id":"","deleted":false,"name":"","icons":null}}`
	CheckResponse(t, x, y)
}

func TestChannelsID(t *testing.T) {
	s := New()
	x := s.ChannelsID("channel")
	y := `"channel"`
	CheckResponse(t, x, y)
}

func TestChannelsMyHistory(t *testing.T) {
	s := New()
	x := s.ChannelsMyHistory("channel", "1234567890")
	y := `{"Filtered":0,"Latest":"","Messages":null,"Oldest":"","Total":0,"Username":""}`
	CheckResponse(t, x, y)
}

func TestChannelsPurgeHistory(t *testing.T) {
	s := New()
	x := s.ChannelsPurgeHistory("channel", "1234567890", true)
	y := `{"Deleted":0,"NotDeleted":0,"Messages":null}`
	CheckResponse(t, x, y)
}

func TestChannelsSetRetention(t *testing.T) {
	s := New()
	x := s.ChannelsSetRetention("channel", 1)
	y := `{"ok":false,"error":"not_authed"}`
	CheckResponse(t, x, y)
}

func TestChannelsSuggestions(t *testing.T) {
	s := New()
	x := s.ChannelsSuggestions()
	y := `{"ok":false,"error":"not_authed","status":{"ok":false},"suggestion_types_tried":null}`
	CheckResponse(t, x, y)
}

func TestChatDelete(t *testing.T) {
	s := New()
	x := s.ChatDelete(MessageArgs{})
	y := `{"ok":false,"error":"not_authed","channel":"","text":"","ts":""}`
	CheckResponse(t, x, y)
}

func TestChatMeMessage(t *testing.T) {
	s := New()
	x := s.ChatMeMessage(MessageArgs{})
	y := `{"ok":false,"error":"not_authed","channel":"","text":"","ts":""}`
	CheckResponse(t, x, y)
}

func TestChatPostMessage(t *testing.T) {
	s := New()
	x := s.ChatPostMessage(MessageArgs{})
	y := `{"ok":false,"error":"not_authed","channel":"","ts":"","message":{"display_as_bot":false}}`
	CheckResponse(t, x, y)
}

func TestChatUpdate(t *testing.T) {
	s := New()
	x := s.ChatUpdate(MessageArgs{})
	y := `{"ok":false,"error":"not_authed","channel":"","ts":"","message":{"display_as_bot":false}}`
	CheckResponse(t, x, y)
}

func TestConversationsArchive(t *testing.T) {
	s := New()
	x := s.ConversationsArchive("channel")
	y := `{"ok":false,"error":"not_authed"}`
	CheckResponse(t, x, y)
}

func TestConversationsCreate(t *testing.T) {
	s := New()
	x := s.ConversationsCreate("channel")
	y := `{"ok":false,"error":"not_authed","channel":{"created":0,"creator":"","id":"","is_archived":false,"is_channel":false,"is_general":false,"is_group":false,"is_member":false,"is_mpim":false,"is_open":false,"last_read":"","latest":{"text":"","ts":"","type":"","user":""},"members":null,"name":"","name_normalized":"","num_members":0,"purpose":{"creator":"","last_set":0,"value":""},"topic":{"creator":"","last_set":0,"value":""},"unread_count":0,"unread_count_display":0}}`
	CheckResponse(t, x, y)
}

func TestConversationsHistory(t *testing.T) {
	s := New()
	x := s.ConversationsHistory(ConversationsHistoryInput{Channel: "channel", Latest: "1234567890"})
	y := `{"ok":false,"error":"not_authed","messages":null,"has_more":false,"pin_count":0,"unread_count_display":0,"response_metadata":{"next_cursor":""}}`
	CheckResponse(t, x, y)
}

func TestConversationsInfo(t *testing.T) {
	s := New()
	x := s.ConversationsInfo("channel")
	y := `{"ok":false,"error":"not_authed","channel":{"created":0,"creator":"","id":"","is_archived":false,"is_channel":false,"is_general":false,"is_group":false,"is_member":false,"is_mpim":false,"is_open":false,"last_read":"","latest":{"text":"","ts":"","type":"","user":""},"members":null,"name":"","name_normalized":"","num_members":0,"purpose":{"creator":"","last_set":0,"value":""},"topic":{"creator":"","last_set":0,"value":""},"unread_count":0,"unread_count_display":0}}`
	CheckResponse(t, x, y)
}

func TestConversationsJoin(t *testing.T) {
	s := New()
	x := s.ConversationsJoin("channel")
	y := `{"ok":false,"error":"not_authed","channel":{"created":0,"creator":"","id":"","is_archived":false,"is_channel":false,"is_general":false,"is_group":false,"is_member":false,"is_mpim":false,"is_open":false,"last_read":"","latest":{"text":"","ts":"","type":"","user":""},"members":null,"name":"","name_normalized":"","num_members":0,"purpose":{"creator":"","last_set":0,"value":""},"topic":{"creator":"","last_set":0,"value":""},"unread_count":0,"unread_count_display":0}}`
	CheckResponse(t, x, y)
}

func TestConversationsInvite(t *testing.T) {
	s := New()
	x := s.ConversationsInvite("channel", "user1", "user2", "user3")
	y := `{"ok":false,"error":"not_authed","channel":{"created":0,"creator":"","id":"","is_archived":false,"is_channel":false,"is_general":false,"is_group":false,"is_member":false,"is_mpim":false,"is_open":false,"last_read":"","latest":{"text":"","ts":"","type":"","user":""},"members":null,"name":"","name_normalized":"","num_members":0,"purpose":{"creator":"","last_set":0,"value":""},"topic":{"creator":"","last_set":0,"value":""},"unread_count":0,"unread_count_display":0}}`
	CheckResponse(t, x, y)
}

func TestConversationsKick(t *testing.T) {
	s := New()
	x := s.ConversationsKick("channel", "user")
	y := `{"ok":false,"error":"not_authed"}`
	CheckResponse(t, x, y)
}

func TestConversationsLeave(t *testing.T) {
	s := New()
	x := s.ConversationsLeave("channel")
	y := `{"ok":false,"error":"not_authed"}`
	CheckResponse(t, x, y)
}

func TestConversationsList(t *testing.T) {
	s := New()
	x := s.ConversationsList(ConversationsListInput{})
	y := `{"ok":false,"error":"not_authed","channels":null}`
	CheckResponse(t, x, y)
}

func TestConversationsRename(t *testing.T) {
	s := New()
	x := s.ConversationsRename("channel", "lennahc")
	y := `{"ok":false,"error":"not_authed","channel":{"created":0,"creator":"","id":"","is_archived":false,"is_channel":false,"is_general":false,"is_group":false,"is_member":false,"is_mpim":false,"is_open":false,"last_read":"","latest":{"text":"","ts":"","type":"","user":""},"members":null,"name":"","name_normalized":"","num_members":0,"purpose":{"creator":"","last_set":0,"value":""},"topic":{"creator":"","last_set":0,"value":""},"unread_count":0,"unread_count_display":0}}`
	CheckResponse(t, x, y)
}

func TestConversationsReplies(t *testing.T) {
	s := New()
	x := s.ConversationsReplies(ConversationsRepliesInput{Channel: "general", Timestamp: "1234567890.123456"})
	y := `{"ok":false,"error":"not_authed","messages":null,"has_more":false,"pin_count":0,"unread_count_display":0,"response_metadata":{"next_cursor":""}}`
	CheckResponse(t, x, y)
}

func TestConversationsSetPurpose(t *testing.T) {
	s := New()
	x := s.ConversationsSetPurpose("channel", "purpose")
	y := `{"ok":false,"error":"not_authed","purpose":""}`
	CheckResponse(t, x, y)
}

func TestConversationsSetTopic(t *testing.T) {
	s := New()
	x := s.ConversationsSetTopic("channel", "topic")
	y := `{"ok":false,"error":"not_authed","topic":""}`
	CheckResponse(t, x, y)
}

func TestConversationsUnarchive(t *testing.T) {
	s := New()
	x := s.ConversationsUnarchive("channel")
	y := `{"ok":false,"error":"not_authed"}`
	CheckResponse(t, x, y)
}
<commit_msg>Remove conversations.unarchive unit test due to API uncertanties<commit_after>package slackapi

import (
	"encoding/json"
	"testing"
)

func CheckResponse(t *testing.T, x interface{}, y string) {
	out, err := json.Marshal(x)
	if err != nil {
		t.Fatal("json fromat;", err)
	}
	if string(out) != y {
		t.Fatalf("invalid json response;\n- %s\n+ %s\n", y, out)
	}
}

func TestAPITest(t *testing.T) {
	s := New()
	x := s.APITest()
	y := `{"ok":true}`
	CheckResponse(t, x, y)
}

func TestAppsList(t *testing.T) {
	s := New()
	x := s.AppsList()
	y := `{"ok":false,"error":"not_authed","apps":null,"cache_ts":""}`
	CheckResponse(t, x, y)
}

func TestAuthRevoke(t *testing.T) {
	s := New()
	x := s.AuthRevoke()
	y := `{"ok":false,"error":"not_authed","revoked":false}`
	CheckResponse(t, x, y)
}

func TestAuthTest(t *testing.T) {
	s := New()
	x, err := s.AuthTest()
	if err != nil {
		t.Fatal(err)
	}
	y := `{"ok":false,"error":"not_authed","team":"","team_id":"","url":"","user":"","user_id":""}`
	CheckResponse(t, x, y)
}

func TestBotsInfo(t *testing.T) {
	s := New()
	x := s.BotsInfo("user")
	y := `{"ok":false,"error":"not_authed","bot":{"id":"","deleted":false,"name":"","icons":null}}`
	CheckResponse(t, x, y)
}

func TestChannelsID(t *testing.T) {
	s := New()
	x := s.ChannelsID("channel")
	y := `"channel"`
	CheckResponse(t, x, y)
}

func TestChannelsMyHistory(t *testing.T) {
	s := New()
	x := s.ChannelsMyHistory("channel", "1234567890")
	y := `{"Filtered":0,"Latest":"","Messages":null,"Oldest":"","Total":0,"Username":""}`
	CheckResponse(t, x, y)
}

func TestChannelsPurgeHistory(t *testing.T) {
	s := New()
	x := s.ChannelsPurgeHistory("channel", "1234567890", true)
	y := `{"Deleted":0,"NotDeleted":0,"Messages":null}`
	CheckResponse(t, x, y)
}

func TestChannelsSetRetention(t *testing.T) {
	s := New()
	x := s.ChannelsSetRetention("channel", 1)
	y := `{"ok":false,"error":"not_authed"}`
	CheckResponse(t, x, y)
}

func TestChannelsSuggestions(t *testing.T) {
	s := New()
	x := s.ChannelsSuggestions()
	y := `{"ok":false,"error":"not_authed","status":{"ok":false},"suggestion_types_tried":null}`
	CheckResponse(t, x, y)
}

func TestChatDelete(t *testing.T) {
	s := New()
	x := s.ChatDelete(MessageArgs{})
	y := `{"ok":false,"error":"not_authed","channel":"","text":"","ts":""}`
	CheckResponse(t, x, y)
}

func TestChatMeMessage(t *testing.T) {
	s := New()
	x := s.ChatMeMessage(MessageArgs{})
	y := `{"ok":false,"error":"not_authed","channel":"","text":"","ts":""}`
	CheckResponse(t, x, y)
}

func TestChatPostMessage(t *testing.T) {
	s := New()
	x := s.ChatPostMessage(MessageArgs{})
	y := `{"ok":false,"error":"not_authed","channel":"","ts":"","message":{"display_as_bot":false}}`
	CheckResponse(t, x, y)
}

func TestChatUpdate(t *testing.T) {
	s := New()
	x := s.ChatUpdate(MessageArgs{})
	y := `{"ok":false,"error":"not_authed","channel":"","ts":"","message":{"display_as_bot":false}}`
	CheckResponse(t, x, y)
}

func TestConversationsArchive(t *testing.T) {
	s := New()
	x := s.ConversationsArchive("channel")
	y := `{"ok":false,"error":"not_authed"}`
	CheckResponse(t, x, y)
}

func TestConversationsCreate(t *testing.T) {
	s := New()
	x := s.ConversationsCreate("channel")
	y := `{"ok":false,"error":"not_authed","channel":{"created":0,"creator":"","id":"","is_archived":false,"is_channel":false,"is_general":false,"is_group":false,"is_member":false,"is_mpim":false,"is_open":false,"last_read":"","latest":{"text":"","ts":"","type":"","user":""},"members":null,"name":"","name_normalized":"","num_members":0,"purpose":{"creator":"","last_set":0,"value":""},"topic":{"creator":"","last_set":0,"value":""},"unread_count":0,"unread_count_display":0}}`
	CheckResponse(t, x, y)
}

func TestConversationsHistory(t *testing.T) {
	s := New()
	x := s.ConversationsHistory(ConversationsHistoryInput{Channel: "channel", Latest: "1234567890"})
	y := `{"ok":false,"error":"not_authed","messages":null,"has_more":false,"pin_count":0,"unread_count_display":0,"response_metadata":{"next_cursor":""}}`
	CheckResponse(t, x, y)
}

func TestConversationsInfo(t *testing.T) {
	s := New()
	x := s.ConversationsInfo("channel")
	y := `{"ok":false,"error":"not_authed","channel":{"created":0,"creator":"","id":"","is_archived":false,"is_channel":false,"is_general":false,"is_group":false,"is_member":false,"is_mpim":false,"is_open":false,"last_read":"","latest":{"text":"","ts":"","type":"","user":""},"members":null,"name":"","name_normalized":"","num_members":0,"purpose":{"creator":"","last_set":0,"value":""},"topic":{"creator":"","last_set":0,"value":""},"unread_count":0,"unread_count_display":0}}`
	CheckResponse(t, x, y)
}

func TestConversationsJoin(t *testing.T) {
	s := New()
	x := s.ConversationsJoin("channel")
	y := `{"ok":false,"error":"not_authed","channel":{"created":0,"creator":"","id":"","is_archived":false,"is_channel":false,"is_general":false,"is_group":false,"is_member":false,"is_mpim":false,"is_open":false,"last_read":"","latest":{"text":"","ts":"","type":"","user":""},"members":null,"name":"","name_normalized":"","num_members":0,"purpose":{"creator":"","last_set":0,"value":""},"topic":{"creator":"","last_set":0,"value":""},"unread_count":0,"unread_count_display":0}}`
	CheckResponse(t, x, y)
}

func TestConversationsInvite(t *testing.T) {
	s := New()
	x := s.ConversationsInvite("channel", "user1", "user2", "user3")
	y := `{"ok":false,"error":"not_authed","channel":{"created":0,"creator":"","id":"","is_archived":false,"is_channel":false,"is_general":false,"is_group":false,"is_member":false,"is_mpim":false,"is_open":false,"last_read":"","latest":{"text":"","ts":"","type":"","user":""},"members":null,"name":"","name_normalized":"","num_members":0,"purpose":{"creator":"","last_set":0,"value":""},"topic":{"creator":"","last_set":0,"value":""},"unread_count":0,"unread_count_display":0}}`
	CheckResponse(t, x, y)
}

func TestConversationsKick(t *testing.T) {
	s := New()
	x := s.ConversationsKick("channel", "user")
	y := `{"ok":false,"error":"not_authed"}`
	CheckResponse(t, x, y)
}

func TestConversationsLeave(t *testing.T) {
	s := New()
	x := s.ConversationsLeave("channel")
	y := `{"ok":false,"error":"not_authed"}`
	CheckResponse(t, x, y)
}

func TestConversationsList(t *testing.T) {
	s := New()
	x := s.ConversationsList(ConversationsListInput{})
	y := `{"ok":false,"error":"not_authed","channels":null}`
	CheckResponse(t, x, y)
}

func TestConversationsRename(t *testing.T) {
	s := New()
	x := s.ConversationsRename("channel", "lennahc")
	y := `{"ok":false,"error":"not_authed","channel":{"created":0,"creator":"","id":"","is_archived":false,"is_channel":false,"is_general":false,"is_group":false,"is_member":false,"is_mpim":false,"is_open":false,"last_read":"","latest":{"text":"","ts":"","type":"","user":""},"members":null,"name":"","name_normalized":"","num_members":0,"purpose":{"creator":"","last_set":0,"value":""},"topic":{"creator":"","last_set":0,"value":""},"unread_count":0,"unread_count_display":0}}`
	CheckResponse(t, x, y)
}

func TestConversationsReplies(t *testing.T) {
	s := New()
	x := s.ConversationsReplies(ConversationsRepliesInput{Channel: "general", Timestamp: "1234567890.123456"})
	y := `{"ok":false,"error":"not_authed","messages":null,"has_more":false,"pin_count":0,"unread_count_display":0,"response_metadata":{"next_cursor":""}}`
	CheckResponse(t, x, y)
}

func TestConversationsSetPurpose(t *testing.T) {
	s := New()
	x := s.ConversationsSetPurpose("channel", "purpose")
	y := `{"ok":false,"error":"not_authed","purpose":""}`
	CheckResponse(t, x, y)
}

func TestConversationsSetTopic(t *testing.T) {
	s := New()
	x := s.ConversationsSetTopic("channel", "topic")
	y := `{"ok":false,"error":"not_authed","topic":""}`
	CheckResponse(t, x, y)
}
<|endoftext|>


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

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

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

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

package examples_test

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

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
	"github.com/golang/glog"
)

func validateObject(obj runtime.Object) (errors []error) {
	ctx := api.NewDefaultContext()
	switch t := obj.(type) {
	case *api.ReplicationController:
		if t.Namespace == "" {
			t.Namespace = api.NamespaceDefault
		}
		errors = validation.ValidateReplicationController(t)
	case *api.ReplicationControllerList:
		for i := range t.Items {
			errors = append(errors, validateObject(&t.Items[i])...)
		}
	case *api.Service:
		if t.Namespace == "" {
			t.Namespace = api.NamespaceDefault
		}
		api.ValidNamespace(ctx, &t.ObjectMeta)
		errors = validation.ValidateService(t)
	case *api.ServiceList:
		for i := range t.Items {
			errors = append(errors, validateObject(&t.Items[i])...)
		}
	case *api.Pod:
		if t.Namespace == "" {
			t.Namespace = api.NamespaceDefault
		}
		api.ValidNamespace(ctx, &t.ObjectMeta)
		errors = validation.ValidatePod(t)
	case *api.PodList:
		for i := range t.Items {
			errors = append(errors, validateObject(&t.Items[i])...)
		}
	default:
		return []error{fmt.Errorf("no validation defined for %#v", obj)}
	}
	return errors
}

func walkJSONFiles(inDir string, fn func(name, path string, data []byte)) error {
	err := filepath.Walk(inDir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() && path != inDir {
			return filepath.SkipDir
		}
		name := filepath.Base(path)
		ext := filepath.Ext(name)
		if ext != "" {
			name = name[:len(name)-len(ext)]
		}
		if !(ext == ".json" || ext == ".yaml") {
			return nil
		}
		glog.Infof("Testing %s", path)
		data, err := ioutil.ReadFile(path)
		if err != nil {
			return err
		}
		fn(name, path, data)
		return nil
	})
	return err
}

func TestExampleObjectSchemas(t *testing.T) {
	cases := map[string]map[string]runtime.Object{
		"../api/examples": {
			"controller":       &api.ReplicationController{},
			"controller-list":  &api.ReplicationControllerList{},
			"pod":              &api.Pod{},
			"pod-list":         &api.PodList{},
			"service":          &api.Service{},
			"external-service": &api.Service{},
			"service-list":     &api.ServiceList{},
		},
		"../examples/guestbook": {
			"frontend-controller":     &api.ReplicationController{},
			"redis-slave-controller":  &api.ReplicationController{},
			"redis-master-controller": &api.ReplicationController{},
			"frontend-service":        &api.Service{},
			"redis-master-service":    &api.Service{},
			"redis-slave-service":     &api.Service{},
		},
		"../examples/guestbook/v1beta3": {
			"frontend-controller":    &api.ReplicationController{},
			"redis-slave-controller": &api.ReplicationController{},
			"redis-master":           &api.ReplicationController{},
			"frontend-service":       &api.Service{},
			"redis-master-service":   &api.Service{},
			"redis-slave-service":    &api.Service{},
		},
		"../examples/guestbook-go": {
			"guestbook-controller":    &api.ReplicationController{},
			"redis-slave-controller":  &api.ReplicationController{},
			"redis-master-controller": &api.ReplicationController{},
			"guestbook-service":       &api.Service{},
			"redis-master-service":    &api.Service{},
			"redis-slave-service":     &api.Service{},
		},
		"../examples/guestbook-go/v1beta3": {
			"guestbook-controller":    &api.ReplicationController{},
			"redis-slave-controller":  &api.ReplicationController{},
			"redis-master-controller": &api.ReplicationController{},
			"guestbook-service":       &api.Service{},
			"redis-master-service":    &api.Service{},
			"redis-slave-service":     &api.Service{},
		},
		"../examples/walkthrough": {
			"pod1": &api.Pod{},
			"pod2": &api.Pod{},
			"pod-with-http-healthcheck": &api.Pod{},
			"service":                   &api.Service{},
			"replication-controller":    &api.ReplicationController{},
		},
		"../examples/update-demo/v1beta1": {
			"kitten-rc":   &api.ReplicationController{},
			"nautilus-rc": &api.ReplicationController{},
		},
		"../examples/update-demo/v1beta3": {
			"kitten-rc":   &api.ReplicationController{},
			"nautilus-rc": &api.ReplicationController{},
		},
	}

	for path, expected := range cases {
		tested := 0
		err := walkJSONFiles(path, func(name, path string, data []byte) {
			expectedType, found := expected[name]
			if !found {
				t.Errorf("%s does not have a test case defined", path)
				return
			}
			tested += 1
			if err := latest.Codec.DecodeInto(data, expectedType); err != nil {
				t.Errorf("%s did not decode correctly: %v\n%s", path, err, string(data))
				return
			}
			if errors := validateObject(expectedType); len(errors) > 0 {
				t.Errorf("%s did not validate correctly: %v", path, errors)
			}
		})
		if err != nil {
			t.Errorf("Expected no error, Got %v", err)
		}
		if tested != len(expected) {
			t.Errorf("Expected %d examples, Got %d", len(expected), tested)
		}
	}
}

var sampleRegexp = regexp.MustCompile("(?ms)^```(?:(?P<type>yaml)\\w*\\n(?P<content>.+?)|\\w*\\n(?P<content>\\{.+?\\}))\\w*\\n^```")
var subsetRegexp = regexp.MustCompile("(?ms)\\.{3}")

func TestReadme(t *testing.T) {
	paths := []string{
		"../README.md",
		"../examples/walkthrough/README.md",
	}

	for _, path := range paths {
		data, err := ioutil.ReadFile(path)
		if err != nil {
			t.Errorf("Unable to read file %s: %v", path, err)
			continue
		}

		matches := sampleRegexp.FindAllStringSubmatch(string(data), -1)
		if matches == nil {
			continue
		}
		for _, match := range matches {
			var content, subtype string
			for i, name := range sampleRegexp.SubexpNames() {
				if name == "type" {
					subtype = match[i]
				}
				if name == "content" && match[i] != "" {
					content = match[i]
				}
			}
			if subtype == "yaml" && subsetRegexp.FindString(content) != "" {
				t.Logf("skipping (%s): \n%s", subtype, content)
				continue
			}

			//t.Logf("testing (%s): \n%s", subtype, content)
			expectedType := &api.Pod{}
			if err := latest.Codec.DecodeInto([]byte(content), expectedType); err != nil {
				t.Errorf("%s did not decode correctly: %v\n%s", path, err, string(content))
				continue
			}
			if errors := validateObject(expectedType); len(errors) > 0 {
				t.Errorf("%s did not validate correctly: %v", path, errors)
			}
			_, err := latest.Codec.Encode(expectedType)
			if err != nil {
				t.Errorf("Could not encode object: %v", err)
				continue
			}
		}
	}
}
<commit_msg>Revert "Revert "Deleting old sample JSON; moving those in use; updating referenc..."<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.

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

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

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

package examples_test

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

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
	"github.com/golang/glog"
)

func validateObject(obj runtime.Object) (errors []error) {
	ctx := api.NewDefaultContext()
	switch t := obj.(type) {
	case *api.ReplicationController:
		if t.Namespace == "" {
			t.Namespace = api.NamespaceDefault
		}
		errors = validation.ValidateReplicationController(t)
	case *api.ReplicationControllerList:
		for i := range t.Items {
			errors = append(errors, validateObject(&t.Items[i])...)
		}
	case *api.Service:
		if t.Namespace == "" {
			t.Namespace = api.NamespaceDefault
		}
		api.ValidNamespace(ctx, &t.ObjectMeta)
		errors = validation.ValidateService(t)
	case *api.ServiceList:
		for i := range t.Items {
			errors = append(errors, validateObject(&t.Items[i])...)
		}
	case *api.Pod:
		if t.Namespace == "" {
			t.Namespace = api.NamespaceDefault
		}
		api.ValidNamespace(ctx, &t.ObjectMeta)
		errors = validation.ValidatePod(t)
	case *api.PodList:
		for i := range t.Items {
			errors = append(errors, validateObject(&t.Items[i])...)
		}
	default:
		return []error{fmt.Errorf("no validation defined for %#v", obj)}
	}
	return errors
}

func walkJSONFiles(inDir string, fn func(name, path string, data []byte)) error {
	err := filepath.Walk(inDir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() && path != inDir {
			return filepath.SkipDir
		}
		name := filepath.Base(path)
		ext := filepath.Ext(name)
		if ext != "" {
			name = name[:len(name)-len(ext)]
		}
		if !(ext == ".json" || ext == ".yaml") {
			return nil
		}
		glog.Infof("Testing %s", path)
		data, err := ioutil.ReadFile(path)
		if err != nil {
			return err
		}
		fn(name, path, data)
		return nil
	})
	return err
}

func TestExampleObjectSchemas(t *testing.T) {
	cases := map[string]map[string]runtime.Object{
		"../docs/getting-started-guides": {
			"pod": &api.Pod{},
		},
		"../cmd/integration": {
			"controller": &api.ReplicationController{},
		},
		"../examples/guestbook": {
			"frontend-controller":     &api.ReplicationController{},
			"redis-slave-controller":  &api.ReplicationController{},
			"redis-master-controller": &api.ReplicationController{},
			"frontend-service":        &api.Service{},
			"redis-master-service":    &api.Service{},
			"redis-slave-service":     &api.Service{},
		},
		"../examples/guestbook/v1beta3": {
			"frontend-controller":    &api.ReplicationController{},
			"redis-slave-controller": &api.ReplicationController{},
			"redis-master":           &api.ReplicationController{},
			"frontend-service":       &api.Service{},
			"redis-master-service":   &api.Service{},
			"redis-slave-service":    &api.Service{},
		},
		"../examples/guestbook-go": {
			"guestbook-controller":    &api.ReplicationController{},
			"redis-slave-controller":  &api.ReplicationController{},
			"redis-master-controller": &api.ReplicationController{},
			"guestbook-service":       &api.Service{},
			"redis-master-service":    &api.Service{},
			"redis-slave-service":     &api.Service{},
		},
		"../examples/guestbook-go/v1beta3": {
			"guestbook-controller":    &api.ReplicationController{},
			"redis-slave-controller":  &api.ReplicationController{},
			"redis-master-controller": &api.ReplicationController{},
			"guestbook-service":       &api.Service{},
			"redis-master-service":    &api.Service{},
			"redis-slave-service":     &api.Service{},
		},
		"../examples/walkthrough": {
			"pod1": &api.Pod{},
			"pod2": &api.Pod{},
			"pod-with-http-healthcheck": &api.Pod{},
			"service":                   &api.Service{},
			"replication-controller":    &api.ReplicationController{},
		},
		"../examples/update-demo/v1beta1": {
			"kitten-rc":   &api.ReplicationController{},
			"nautilus-rc": &api.ReplicationController{},
		},
		"../examples/update-demo/v1beta3": {
			"kitten-rc":   &api.ReplicationController{},
			"nautilus-rc": &api.ReplicationController{},
		},
	}

	for path, expected := range cases {
		tested := 0
		err := walkJSONFiles(path, func(name, path string, data []byte) {
			expectedType, found := expected[name]
			if !found {
				t.Errorf("%s does not have a test case defined", path)
				return
			}
			tested += 1
			if err := latest.Codec.DecodeInto(data, expectedType); err != nil {
				t.Errorf("%s did not decode correctly: %v\n%s", path, err, string(data))
				return
			}
			if errors := validateObject(expectedType); len(errors) > 0 {
				t.Errorf("%s did not validate correctly: %v", path, errors)
			}
		})
		if err != nil {
			t.Errorf("Expected no error, Got %v", err)
		}
		if tested != len(expected) {
			t.Errorf("Expected %d examples, Got %d", len(expected), tested)
		}
	}
}

var sampleRegexp = regexp.MustCompile("(?ms)^```(?:(?P<type>yaml)\\w*\\n(?P<content>.+?)|\\w*\\n(?P<content>\\{.+?\\}))\\w*\\n^```")
var subsetRegexp = regexp.MustCompile("(?ms)\\.{3}")

func TestReadme(t *testing.T) {
	paths := []string{
		"../README.md",
		"../examples/walkthrough/README.md",
	}

	for _, path := range paths {
		data, err := ioutil.ReadFile(path)
		if err != nil {
			t.Errorf("Unable to read file %s: %v", path, err)
			continue
		}

		matches := sampleRegexp.FindAllStringSubmatch(string(data), -1)
		if matches == nil {
			continue
		}
		for _, match := range matches {
			var content, subtype string
			for i, name := range sampleRegexp.SubexpNames() {
				if name == "type" {
					subtype = match[i]
				}
				if name == "content" && match[i] != "" {
					content = match[i]
				}
			}
			if subtype == "yaml" && subsetRegexp.FindString(content) != "" {
				t.Logf("skipping (%s): \n%s", subtype, content)
				continue
			}

			//t.Logf("testing (%s): \n%s", subtype, content)
			expectedType := &api.Pod{}
			if err := latest.Codec.DecodeInto([]byte(content), expectedType); err != nil {
				t.Errorf("%s did not decode correctly: %v\n%s", path, err, string(content))
				continue
			}
			if errors := validateObject(expectedType); len(errors) > 0 {
				t.Errorf("%s did not validate correctly: %v", path, errors)
			}
			_, err := latest.Codec.Encode(expectedType)
			if err != nil {
				t.Errorf("Could not encode object: %v", err)
				continue
			}
		}
	}
}
<|endoftext|>


<commit_before>package goptions

import (
	"fmt"
	"os"
	"time"
)

func ExampleFlagSet_PrintHelp() {
	options := struct {
		Server   string        `goptions:"-s, --server, obligatory, description='Server to connect to'"`
		Password string        `goptions:"-p, --password, description='Don\\'t prompt for password'"`
		Timeout  time.Duration `goptions:"-t, --timeout, description='Connection timeout in seconds'"`
		Help     Help          `goptions:"-h, --help, description='Show this help'"`

		Verbs
		Execute struct {
			Command string   `goptions:"--command, mutexgroup='input', description='Command to exectute', obligatory"`
			Script  *os.File `goptions:"--script, mutexgroup='input', description='Script to exectute', rdonly"`
		} `goptions:"execute"`
		Delete struct {
			Path  string `goptions:"-n, --name, obligatory, description='Name of the entity to be deleted'"`
			Force bool   `goptions:"-f, --force, description='Force removal'"`
		} `goptions:"delete"`
	}{ // Default values goes here
		Timeout: 10 * time.Second,
	}

	args := []string{"--help"}
	fs := NewFlagSet("goptions", &options)
	err := fs.Parse(args)
	if err == ErrHelpRequest {
		fs.PrintHelp(os.Stdout)
		return
	} else if err != nil {
		fmt.Printf("Failure: %s", err)
	}

	// Output:
	// Usage: goptions [global options] <verb> [verb options]
	//
	// Global options:
	//         -s, --server   Server to connect to (*)
	//         -p, --password Don't prompt for password
	//         -t, --timeout  Connection timeout in seconds (default: 10s)
	//         -h, --help     Show this help
	//
	// Verbs:
	//     delete:
	//         -n, --name     Name of the entity to be deleted (*)
	//         -f, --force    Force removal
	//     execute:
	//             --command  Command to exectute (*)
	//             --script   Script to exectute
}
<commit_msg>Add examples for verbs and remainder (closes #6)<commit_after>package goptions

import (
	"fmt"
	"os"
	"time"
)

func ExampleFlagSet_PrintHelp() {
	options := struct {
		Server   string        `goptions:"-s, --server, obligatory, description='Server to connect to'"`
		Password string        `goptions:"-p, --password, description='Don\\'t prompt for password'"`
		Timeout  time.Duration `goptions:"-t, --timeout, description='Connection timeout in seconds'"`
		Help     Help          `goptions:"-h, --help, description='Show this help'"`

		Verbs
		Execute struct {
			Command string   `goptions:"--command, mutexgroup='input', description='Command to exectute', obligatory"`
			Script  *os.File `goptions:"--script, mutexgroup='input', description='Script to exectute', rdonly"`
		} `goptions:"execute"`
		Delete struct {
			Path  string `goptions:"-n, --name, obligatory, description='Name of the entity to be deleted'"`
			Force bool   `goptions:"-f, --force, description='Force removal'"`
		} `goptions:"delete"`
	}{ // Default values goes here
		Timeout: 10 * time.Second,
	}

	args := []string{"--help"}
	fs := NewFlagSet("goptions", &options)
	err := fs.Parse(args)
	if err == ErrHelpRequest {
		fs.PrintHelp(os.Stdout)
		return
	} else if err != nil {
		fmt.Printf("Failure: %s", err)
	}

	// Output:
	// Usage: goptions [global options] <verb> [verb options]
	//
	// Global options:
	//         -s, --server   Server to connect to (*)
	//         -p, --password Don't prompt for password
	//         -t, --timeout  Connection timeout in seconds (default: 10s)
	//         -h, --help     Show this help
	//
	// Verbs:
	//     delete:
	//         -n, --name     Name of the entity to be deleted (*)
	//         -f, --force    Force removal
	//     execute:
	//             --command  Command to exectute (*)
	//             --script   Script to exectute
}

func ExampleVerbs() {
	options := struct {
		ImportantFlag string        `goptions:"-f, --flag, description='Important flag, obligatory'"`
		Password      string        `goptions:"-p, --password, description='Don\\'t prompt for password'"`
		Timeout       time.Duration `goptions:"-t, --timeout, description='Connection timeout in seconds'"`
		Help          Help          `goptions:"-h, --help, description='Show this help'"`

		Verb    Verbs
		Execute struct {
			Command string   `goptions:"--command, mutexgroup='input', description='Command to exectute', obligatory"`
			Script  *os.File `goptions:"--script, mutexgroup='input', description='Script to exectute', rdonly"`
		} `goptions:"execute"`
		Delete struct {
			Path  string `goptions:"-n, --name, obligatory, description='Name of the entity to be deleted'"`
			Force bool   `goptions:"-f, --force, description='Force removal'"`
		} `goptions:"delete"`
	}{ // Default values goes here
		Timeout: 10 * time.Second,
	}

	args := []string{"delete", "-n", "/usr/bin"}
	fs := NewFlagSet("goptions", &options)
	_ = fs.Parse(args)
	// Error handling omitted
	fmt.Printf("Selected verb: %s", options.Verb)

	// Output:
	// Selected verb: delete
}

func ExampleRemainder() {
	options := struct {
		Username  string `goptions:"-u, --user, obligatory, description='Name of the user'"`
		Remainder Remainder
	}{}

	args := []string{"-u", "surma", "some", "more", "args"}
	fs := NewFlagSet("goptions", &options)
	_ = fs.Parse(args)
	// Error handling omitted
	fmt.Printf("Remainder: %#v", options.Remainder)

	// Output:
	// Remainder: goptions.Remainder{"some", "more", "args"}
}
<|endoftext|>


<commit_before>// Package logging provides a fast, asynchronous request logger which outputs
// NCSA/Apache combined logs.
package logging

import (
	"fmt"
	"io"
	"net/http"
	"strings"
	"time"
)

// A LoggingHandler is a HTTP handler which proxies requests to an underlying
// handler and logs the results.
type LoggingHandler struct {
	clock   clock
	w       io.Writer
	handler http.Handler
	buffer  chan string
	quit    chan struct{}
}

// Wrap returns the underlying handler, wrapped in a LoggingHandler which will
// write to the given Writer. N.B.: You must call Start() on the result before
// using it.
func Wrap(h http.Handler, w io.Writer) *LoggingHandler {
	return &LoggingHandler{
		clock:   time.Now,
		w:       w,
		handler: h,
		buffer:  make(chan string, 1000),
		quit:    make(chan struct{}),
	}
}

// Start creates a goroutine to handle the logging IO.
func (al *LoggingHandler) Start() {
	go func() {
		for s := range al.buffer {
			fmt.Fprint(al.w, s)
		}
		close(al.quit)
	}()
}

// Stop closes the internal channel used to buffer log statements and waits for
// the IO goroutine to complete.
func (al *LoggingHandler) Stop() {
	close(al.buffer)
	<-al.quit
}

func (al *LoggingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	wrapper := &responseWrapper{w: w, status: 200}

	start := al.clock()
	al.handler.ServeHTTP(wrapper, r)
	end := al.clock()

	remoteAddr := r.RemoteAddr
	if index := strings.LastIndex(remoteAddr, ":"); index != -1 {
		remoteAddr = remoteAddr[:index]
	}

	if s := r.Header.Get(xForwardedFor); s != "" {
		remoteAddr = s
	}

	referer := r.Referer()
	if "" == referer {
		referer = "-"
	}

	userAgent := r.UserAgent()
	if "" == userAgent {
		userAgent = "-"
	}

	al.buffer <- fmt.Sprintf(
		"%s %s %s [%s] \"%s %s %s\" %d %d %q %q %d %q\n",
		remoteAddr,
		"-", // We're not supporting identd, sorry.
		"-", // We're also not supporting basic auth.
		start.In(time.UTC).Format(apacheFormat),
		r.Method,
		r.RequestURI,
		r.Proto,
		wrapper.status,
		0,
		referer,
		userAgent,
		end.Sub(start).Nanoseconds()/int64(time.Millisecond),
		r.Header.Get(xRequestID),
	)
}

const (
	apacheFormat  = "02/Jan/2006:15:04:05 -0700"
	xRequestID    = "X-Request-Id"
	xForwardedFor = "X-Forwarded-For"
)

type responseWrapper struct {
	w      http.ResponseWriter
	status int
}

func (w *responseWrapper) Header() http.Header {
	return w.w.Header()
}

func (w *responseWrapper) Write(b []byte) (int, error) {
	return w.w.Write(b)
}

func (w *responseWrapper) WriteHeader(status int) {
	w.status = status
	w.w.WriteHeader(status)
}

type clock func() time.Time
<commit_msg>Make logging responseWrapper proxy Hijack()<commit_after>// Package logging provides a fast, asynchronous request logger which outputs
// NCSA/Apache combined logs.
package logging

import (
	"bufio"
	"fmt"
	"io"
	"net"
	"net/http"
	"strings"
	"time"
)

// A LoggingHandler is a HTTP handler which proxies requests to an underlying
// handler and logs the results.
type LoggingHandler struct {
	clock   clock
	w       io.Writer
	handler http.Handler
	buffer  chan string
	quit    chan struct{}
}

// Wrap returns the underlying handler, wrapped in a LoggingHandler which will
// write to the given Writer. N.B.: You must call Start() on the result before
// using it.
func Wrap(h http.Handler, w io.Writer) *LoggingHandler {
	return &LoggingHandler{
		clock:   time.Now,
		w:       w,
		handler: h,
		buffer:  make(chan string, 1000),
		quit:    make(chan struct{}),
	}
}

// Start creates a goroutine to handle the logging IO.
func (al *LoggingHandler) Start() {
	go func() {
		for s := range al.buffer {
			fmt.Fprint(al.w, s)
		}
		close(al.quit)
	}()
}

// Stop closes the internal channel used to buffer log statements and waits for
// the IO goroutine to complete.
func (al *LoggingHandler) Stop() {
	close(al.buffer)
	<-al.quit
}

func (al *LoggingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	wrapper := &responseWrapper{w: w, status: 200}

	start := al.clock()
	al.handler.ServeHTTP(wrapper, r)
	end := al.clock()

	remoteAddr := r.RemoteAddr
	if index := strings.LastIndex(remoteAddr, ":"); index != -1 {
		remoteAddr = remoteAddr[:index]
	}

	if s := r.Header.Get(xForwardedFor); s != "" {
		remoteAddr = s
	}

	referer := r.Referer()
	if "" == referer {
		referer = "-"
	}

	userAgent := r.UserAgent()
	if "" == userAgent {
		userAgent = "-"
	}

	al.buffer <- fmt.Sprintf(
		"%s %s %s [%s] \"%s %s %s\" %d %d %q %q %d %q\n",
		remoteAddr,
		"-", // We're not supporting identd, sorry.
		"-", // We're also not supporting basic auth.
		start.In(time.UTC).Format(apacheFormat),
		r.Method,
		r.RequestURI,
		r.Proto,
		wrapper.status,
		0,
		referer,
		userAgent,
		end.Sub(start).Nanoseconds()/int64(time.Millisecond),
		r.Header.Get(xRequestID),
	)
}

const (
	apacheFormat  = "02/Jan/2006:15:04:05 -0700"
	xRequestID    = "X-Request-Id"
	xForwardedFor = "X-Forwarded-For"
)

type responseWrapper struct {
	w      http.ResponseWriter
	status int
}

func (w *responseWrapper) Header() http.Header {
	return w.w.Header()
}

func (w *responseWrapper) Write(b []byte) (int, error) {
	return w.w.Write(b)
}

func (w *responseWrapper) WriteHeader(status int) {
	w.status = status
	w.w.WriteHeader(status)
}

func (w *responseWrapper) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	if hijacker, ok := w.w.(http.Hijacker); ok {
		return hijacker.Hijack()
	} else {
		panic("http-handlers: ResponseWriter does not implement http.Hijacker")
	}
}

type clock func() time.Time
<|endoftext|>


<commit_before>// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.

package command

import (
	"fmt"
	"io"
	"os"
	"strconv"
	"text/tabwriter"

	"github.com/urfave/cli"
	"github.com/vmware/photon-controller-go-sdk/photon"

	"github.com/vmware/photon-controller-cli/photon/client"
	"github.com/vmware/photon-controller-cli/photon/utils"
)

const (
	ROUTED   = "ROUTED"
	ISOLATED = "ISOLATED"
)

func createVirtualNetwork(c *cli.Context, w io.Writer) error {
	err := checkArgCount(c, 0)
	if err != nil {
		return err
	}

	name := c.String("name")
	description := c.String("description")
	routingType := c.String("routingType")
	sizeStr := c.String("size")
	staticIpSizeStr := c.String("staticIpSize")
	projectId := c.String("projectId")

	if len(projectId) == 0 {
		tenant, err := verifyTenant("")
		if err != nil {
			return err
		}

		project, err := verifyProject(tenant.ID, "")
		if err != nil {
			return err
		}

		projectId = project.ID
	}

	if !c.GlobalIsSet("non-interactive") {
		name, err = askForInput("Network name: ", name)
		if err != nil {
			return err
		}
		description, err = askForInput("Description of network: ", description)
		if err != nil {
			return err
		}
		routingType, err = askForInput("Routing type of network: ", routingType)
		if err != nil {
			return err
		}
		projectId, err = askForInput("Project ID that network belongs to: ", projectId)
		if err != nil {
			return err
		}
		sizeStr, err = askForInput("Size of IP pool of the network (must be power of 2, at least 8): ", sizeStr)
		if err != nil {
			return err
		}
		staticIpSizeStr, err = askForInput("Size of the static IP pool (must be less than size of IP pool): ",
			staticIpSizeStr)
		if err != nil {
			return err
		}
	}

	if len(name) == 0 {
		return fmt.Errorf("Please provide network name")
	}
	if routingType != ROUTED && routingType != ISOLATED {
		return fmt.Errorf("Please choose the correct routing type for network (ROUTED or ISOLATED)")
	}
	size, err := strconv.Atoi(sizeStr)
	if err != nil {
		return err
	}
	if size < 8 {
		return fmt.Errorf("Network size must be at least 8")
	}
	staticIpSize, err := strconv.Atoi(staticIpSizeStr)
	if err != nil {
		return err
	}

	createSpec := &photon.VirtualSubnetCreateSpec{
		Name:                 name,
		Description:          description,
		RoutingType:          routingType,
		Size:                 size,
		ReservedStaticIpSize: staticIpSize,
	}

	client.Photonclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	task, err := client.Photonclient.VirtualSubnets.Create(projectId, createSpec)
	if err != nil {
		return err
	}

	id, err := waitOnTaskOperation(task.ID, c)
	if err != nil {
		return err
	}

	if utils.NeedsFormatting(c) {
		network, err := client.Photonclient.Networks.Get(id)
		if err != nil {
			return err
		}
		utils.FormatObject(network, w, c)
	}

	return nil
}

func listVirtualNetworks(c *cli.Context, w io.Writer) error {
	err := checkArgCount(c, 0)
	if err != nil {
		return err
	}
	client.Photonclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	name := c.String("name")
	options := &photon.VirtualSubnetGetOptions{
		Name: name,
	}

	projectId := c.String("projectId")
	if len(projectId) == 0 {
		tenant, err := verifyTenant("")
		if err != nil {
			return err
		}

		project, err := verifyProject(tenant.ID, "")
		if err != nil {
			return err
		}

		projectId = project.ID
	}

	networks, err := client.Photonclient.VirtualSubnets.GetAll(projectId, options)
	if err != nil {
		return err
	}

	if c.GlobalIsSet("non-interactive") {
		for _, network := range networks.Items {
			fmt.Printf("%s\t%s\t%s\t%s\t%s\t%t\t%s\t%s\t%s\t%s\t%s\t%s\n", network.ID, network.Name, network.State,
				network.Description, network.RoutingType, network.IsDefault, network.Cidr, network.LowIpDynamic,
				network.HighIpDynamic, network.LowIpStatic, network.HighIpStatic, network.ReservedIpList)
		}
	} else if utils.NeedsFormatting(c) {
		utils.FormatObjects(networks.Items, w, c)
	} else {
		w := new(tabwriter.Writer)
		w.Init(os.Stdout, 4, 4, 2, ' ', 0)
		fmt.Fprintf(w, "ID\tName\tState\tDescriptions\tRoutingType\tIsDefault\tCIDR\tLowDynamicIP\tHighDynamicIP"+
			"\tLowStaticIP\tHighStaticIP\tReservedIpList\n")
		for _, network := range networks.Items {
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%t\t%s\t%s\t%s\t%s\t%s\t%s\n", network.ID, network.Name, network.State,
				network.Description, network.RoutingType, network.IsDefault, network.Cidr, network.LowIpDynamic,
				network.HighIpDynamic, network.LowIpStatic, network.HighIpStatic, network.ReservedIpList)
		}
		err = w.Flush()
		if err != nil {
			return err
		}
		fmt.Printf("Total: %d\n", len(networks.Items))
	}

	return nil
}

func showVirtualNetwork(c *cli.Context, w io.Writer) error {
	err := checkArgCount(c, 1)
	if err != nil {
		return err
	}
	id := c.Args().First()

	client.Photonclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	network, err := client.Photonclient.VirtualSubnets.Get(id)
	if err != nil {
		return err
	}

	if c.GlobalIsSet("non-interactive") {
		fmt.Printf("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", network.ID, network.Name, network.State,
			network.Description, network.RoutingType, network.IsDefault, network.Cidr, network.LowIpDynamic,
			network.HighIpDynamic, network.LowIpStatic, network.HighIpStatic, network.ReservedIpList)
	} else if utils.NeedsFormatting(c) {
		utils.FormatObject(network, w, c)
	} else {
		fmt.Printf("Network ID: %s\n", network.ID)
		fmt.Printf("  Name:             %s\n", network.Name)
		fmt.Printf("  State:            %s\n", network.State)
		fmt.Printf("  Description:      %s\n", network.Description)
		fmt.Printf("  Routing Type:     %s\n", network.RoutingType)
		fmt.Printf("  Is Default:       %s\n", network.IsDefault)
		fmt.Printf("  CIDR:             %s\n", network.Cidr)
		fmt.Printf("  Start Dynamic IP: %s\n", network.LowIpDynamic)
		fmt.Printf("  End Dynamic IP:   %s\n", network.HighIpDynamic)
		fmt.Printf("  Start Static IP:  %s\n", network.LowIpStatic)
		fmt.Printf("  End Static IP:    %s\n", network.HighIpStatic)
		fmt.Printf("  Reserved IP List: %s\n", network.ReservedIpList)
	}

	return nil
}
<commit_msg>Fix formatting of NSX network<commit_after>// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.

package command

import (
	"fmt"
	"io"
	"os"
	"strconv"
	"text/tabwriter"

	"github.com/urfave/cli"
	"github.com/vmware/photon-controller-go-sdk/photon"

	"github.com/vmware/photon-controller-cli/photon/client"
	"github.com/vmware/photon-controller-cli/photon/utils"
)

const (
	ROUTED   = "ROUTED"
	ISOLATED = "ISOLATED"
)

func createVirtualNetwork(c *cli.Context, w io.Writer) error {
	err := checkArgCount(c, 0)
	if err != nil {
		return err
	}

	name := c.String("name")
	description := c.String("description")
	routingType := c.String("routingType")
	sizeStr := c.String("size")
	staticIpSizeStr := c.String("staticIpSize")
	projectId := c.String("projectId")

	if len(projectId) == 0 {
		tenant, err := verifyTenant("")
		if err != nil {
			return err
		}

		project, err := verifyProject(tenant.ID, "")
		if err != nil {
			return err
		}

		projectId = project.ID
	}

	if !c.GlobalIsSet("non-interactive") {
		name, err = askForInput("Network name: ", name)
		if err != nil {
			return err
		}
		description, err = askForInput("Description of network: ", description)
		if err != nil {
			return err
		}
		routingType, err = askForInput("Routing type of network: ", routingType)
		if err != nil {
			return err
		}
		projectId, err = askForInput("Project ID that network belongs to: ", projectId)
		if err != nil {
			return err
		}
		sizeStr, err = askForInput("Size of IP pool of the network (must be power of 2, at least 8): ", sizeStr)
		if err != nil {
			return err
		}
		staticIpSizeStr, err = askForInput("Size of the static IP pool (must be less than size of IP pool): ",
			staticIpSizeStr)
		if err != nil {
			return err
		}
	}

	if len(name) == 0 {
		return fmt.Errorf("Please provide network name")
	}
	if routingType != ROUTED && routingType != ISOLATED {
		return fmt.Errorf("Please choose the correct routing type for network (ROUTED or ISOLATED)")
	}
	size, err := strconv.Atoi(sizeStr)
	if err != nil {
		return err
	}
	if size < 8 {
		return fmt.Errorf("Network size must be at least 8")
	}
	staticIpSize, err := strconv.Atoi(staticIpSizeStr)
	if err != nil {
		return err
	}

	createSpec := &photon.VirtualSubnetCreateSpec{
		Name:                 name,
		Description:          description,
		RoutingType:          routingType,
		Size:                 size,
		ReservedStaticIpSize: staticIpSize,
	}

	client.Photonclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	task, err := client.Photonclient.VirtualSubnets.Create(projectId, createSpec)
	if err != nil {
		return err
	}

	id, err := waitOnTaskOperation(task.ID, c)
	if err != nil {
		return err
	}

	if utils.NeedsFormatting(c) {
		network, err := client.Photonclient.Networks.Get(id)
		if err != nil {
			return err
		}
		utils.FormatObject(network, w, c)
	}

	return nil
}

func listVirtualNetworks(c *cli.Context, w io.Writer) error {
	err := checkArgCount(c, 0)
	if err != nil {
		return err
	}
	client.Photonclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	name := c.String("name")
	options := &photon.VirtualSubnetGetOptions{
		Name: name,
	}

	projectId := c.String("projectId")
	if len(projectId) == 0 {
		tenant, err := verifyTenant("")
		if err != nil {
			return err
		}

		project, err := verifyProject(tenant.ID, "")
		if err != nil {
			return err
		}

		projectId = project.ID
	}

	networks, err := client.Photonclient.VirtualSubnets.GetAll(projectId, options)
	if err != nil {
		return err
	}

	if c.GlobalIsSet("non-interactive") {
		for _, network := range networks.Items {
			fmt.Printf("%s\t%s\t%s\t%s\t%s\t%t\t%s\t%s\t%s\t%s\t%s\t%s\n", network.ID, network.Name, network.State,
				network.Description, network.RoutingType, network.IsDefault, network.Cidr, network.LowIpDynamic,
				network.HighIpDynamic, network.LowIpStatic, network.HighIpStatic, network.ReservedIpList)
		}
	} else if utils.NeedsFormatting(c) {
		utils.FormatObjects(networks.Items, w, c)
	} else {
		w := new(tabwriter.Writer)
		w.Init(os.Stdout, 4, 4, 2, ' ', 0)
		fmt.Fprintf(w, "ID\tName\tState\tDescriptions\tRoutingType\tIsDefault\tCIDR\tLowDynamicIP\tHighDynamicIP"+
			"\tLowStaticIP\tHighStaticIP\tReservedIpList\n")
		for _, network := range networks.Items {
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%t\t%s\t%s\t%s\t%s\t%s\t%s\n", network.ID, network.Name, network.State,
				network.Description, network.RoutingType, network.IsDefault, network.Cidr, network.LowIpDynamic,
				network.HighIpDynamic, network.LowIpStatic, network.HighIpStatic, network.ReservedIpList)
		}
		err = w.Flush()
		if err != nil {
			return err
		}
		fmt.Printf("Total: %d\n", len(networks.Items))
	}

	return nil
}

func showVirtualNetwork(c *cli.Context, w io.Writer) error {
	err := checkArgCount(c, 1)
	if err != nil {
		return err
	}
	id := c.Args().First()

	client.Photonclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	network, err := client.Photonclient.VirtualSubnets.Get(id)
	if err != nil {
		return err
	}

	if c.GlobalIsSet("non-interactive") {
		fmt.Printf("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", network.ID, network.Name, network.State,
			network.Description, network.RoutingType, network.IsDefault, network.Cidr, network.LowIpDynamic,
			network.HighIpDynamic, network.LowIpStatic, network.HighIpStatic, network.ReservedIpList)
	} else if utils.NeedsFormatting(c) {
		utils.FormatObject(network, w, c)
	} else {
		fmt.Printf("Network ID: %s\n", network.ID)
		fmt.Printf("  Name:             %s\n", network.Name)
		fmt.Printf("  State:            %s\n", network.State)
		fmt.Printf("  Description:      %s\n", network.Description)
		fmt.Printf("  Routing Type:     %s\n", network.RoutingType)
		fmt.Printf("  Is Default:       %t\n", network.IsDefault)
		fmt.Printf("  CIDR:             %s\n", network.Cidr)
		fmt.Printf("  Start Dynamic IP: %s\n", network.LowIpDynamic)
		fmt.Printf("  End Dynamic IP:   %s\n", network.HighIpDynamic)
		fmt.Printf("  Start Static IP:  %s\n", network.LowIpStatic)
		fmt.Printf("  End Static IP:    %s\n", network.HighIpStatic)
		fmt.Printf("  Reserved IP List: %s\n", network.ReservedIpList)
	}

	return nil
}
<|endoftext|>


<commit_before>package cfg

import "github.com/fly/config"

// Config describes the config object..
type Config struct {
	TeamPlayerCount int      `yaml:"team_player_count"`
	TeamCount       int      `yaml:"teams"`
	Checks          []string `yaml:"checks"`
}

// NewConfig reads configuration from environment and returns a config object.
func NewConfig() (c *config.Config, err error) {
	c, err = config.NewConfigFromNamespace("fly", "matchmaking")
	if err != nil {
		return
	}
	return
}
<commit_msg>remove config.NewConfig()<commit_after>package cfg

// Config describes the config object..
type Config struct {
	TeamPlayerCount int      `yaml:"team_player_count"`
	TeamCount       int      `yaml:"teams"`
	Checks          []string `yaml:"checks"`
}
<|endoftext|>


<commit_before>/*
Copyright 2017 Google Inc.

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

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

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
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 config

import (
	"fmt"
	"net"
	"strconv"
	"strings"

	"github.com/golang/glog"

	"github.com/GoogleCloudPlatform/k8s-stackdriver/prometheus-to-sd/flags"
)

// SourceConfig contains data specific for scraping one component.
type SourceConfig struct {
	Component            string
	Protocol             string
	Host                 string
	Port                 uint
	Path                 string
	AuthConfig           AuthConfig
	Whitelisted          []string
	WhitelistedLabelsMap map[string]map[string]bool
	PodConfig            PodConfig
	MetricsPrefix        string
}

const defaultMetricsPath = "/metrics"

var validWhitelistedLabels = map[string]bool{"containerNameLabel": true, "namespaceIdLabel": true, "podIdLabel": true}

// newSourceConfig creates a new SourceConfig based on string representation of fields.
func newSourceConfig(component, protocol, host, port, path string, auth AuthConfig, whitelisted, metricsPrefix string, podConfig PodConfig, whitelistedLabelsMap map[string]map[string]bool) (*SourceConfig, error) {
	if port == "" {
		return nil, fmt.Errorf("No port provided.")
	}
	if path == "" || path == "/" {
		path = defaultMetricsPath
	}

	portNum, err := strconv.ParseUint(port, 10, 32)
	if err != nil {
		return nil, err
	}

	if len(protocol) == 0 {
		if portNum == 443 {
			protocol = "https"
		} else {
			protocol = "http"
		}
	}

	var whitelistedList []string
	if whitelisted != "" {
		whitelistedList = strings.Split(whitelisted, ",")
	}

	return &SourceConfig{
		Component:            component,
		Protocol:             protocol,
		Host:                 host,
		Port:                 uint(portNum),
		Path:                 path,
		AuthConfig:           auth,
		Whitelisted:          whitelistedList,
		WhitelistedLabelsMap: whitelistedLabelsMap,
		PodConfig:            podConfig,
		MetricsPrefix:        metricsPrefix,
	}, nil
}

// parseSourceConfig creates a new SourceConfig based on the provided flags.Uri instance.
func parseSourceConfig(uri flags.Uri, podId, namespaceId string) (*SourceConfig, error) {
	host, port, err := net.SplitHostPort(uri.Val.Host)
	if err != nil {
		return nil, err
	}

	component := uri.Key
	values := uri.Val.Query()
	protocol := uri.Val.Scheme
	path := uri.Val.Path
	whitelisted := values.Get("whitelisted")
	podIdLabel := values.Get("podIdLabel")
	namespaceIdLabel := values.Get("namespaceIdLabel")
	containerNamelabel := values.Get("containerNamelabel")
	metricsPrefix := values.Get("metricsPrefix")
	auth, err := parseAuthConfig(uri.Val)
	if err != nil {
		return nil, err
	}
	podConfig := NewPodConfig(podId, namespaceId, podIdLabel, namespaceIdLabel, containerNamelabel)

	whitelistedLabelsMap, err := parseWhitelistedLabels(values.Get("whitelistedLabels"))
	if err != nil {
		return nil, err
	}
	return newSourceConfig(component, protocol, host, port, path, *auth, whitelisted, metricsPrefix, podConfig, whitelistedLabelsMap)
}

// UpdateWhitelistedMetrics sets passed list as a list of whitelisted metrics.
func (config *SourceConfig) UpdateWhitelistedMetrics(list []string) {
	config.Whitelisted = list
}

// SourceConfigsFromFlags creates a slice of SourceConfig's base on the provided flags.
func SourceConfigsFromFlags(source flags.Uris, podId *string, namespaceId *string, defaultMetricsPrefix string) []*SourceConfig {
	var sourceConfigs []*SourceConfig
	for _, c := range source {
		if sourceConfig, err := parseSourceConfig(c, *podId, *namespaceId); err != nil {
			glog.Fatalf("Error while parsing source config flag %v: %v", c, err)
		} else {
			if sourceConfig.MetricsPrefix == "" {
				sourceConfig.MetricsPrefix = defaultMetricsPrefix
			}
			sourceConfigs = append(sourceConfigs, sourceConfig)
		}
	}
	return sourceConfigs
}

// parseWhitelistedLabels extracts the labels and their corresponding whitelisted values that will be filtered for.
func parseWhitelistedLabels(whitelistedLabels string) (map[string]map[string]bool, error) {
	// whitelistedLabels  is of the format whitelistedLabel1:val11,val12,etc.|whitelistedLabel2:val21
	labelsMap := make(map[string]map[string]bool)
	// URL.Query().Get() will return "" if whitelistedLabels is not specified
	if whitelistedLabels == "" {
		return labelsMap, nil
	}
	labelVals := strings.Split(whitelistedLabels, "|")
	for _, labelVal := range labelVals {
		labelAndValueParts := strings.Split(labelVal, ":")
		if len(labelAndValueParts) != 2 {
			return nil, fmt.Errorf("Incorrectly formatted whitelisted label and values: %v", labelVal)
		}
		labelKey := labelAndValueParts[0]
		if validWhitelistedLabels[labelKey] {
			labelsMap[labelKey] = make(map[string]bool)
			for _, val := range strings.Split(labelAndValueParts[1], ",") {
				labelsMap[labelAndValueParts[0]][val] = true
			}
		} else {
			return nil, fmt.Errorf("Filtering against label %v is unsupported. Only containerNameLabel, namespaceIdLabel, and podIdLabel are supported.", labelKey)
		}
	}
	return labelsMap, nil
}
<commit_msg>Fix container name label typo.<commit_after>/*
Copyright 2017 Google Inc.

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

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

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
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 config

import (
	"fmt"
	"net"
	"strconv"
	"strings"

	"github.com/golang/glog"

	"github.com/GoogleCloudPlatform/k8s-stackdriver/prometheus-to-sd/flags"
)

// SourceConfig contains data specific for scraping one component.
type SourceConfig struct {
	Component            string
	Protocol             string
	Host                 string
	Port                 uint
	Path                 string
	AuthConfig           AuthConfig
	Whitelisted          []string
	WhitelistedLabelsMap map[string]map[string]bool
	PodConfig            PodConfig
	MetricsPrefix        string
}

const defaultMetricsPath = "/metrics"

var validWhitelistedLabels = map[string]bool{"containerNameLabel": true, "namespaceIdLabel": true, "podIdLabel": true}

// newSourceConfig creates a new SourceConfig based on string representation of fields.
func newSourceConfig(component, protocol, host, port, path string, auth AuthConfig, whitelisted, metricsPrefix string, podConfig PodConfig, whitelistedLabelsMap map[string]map[string]bool) (*SourceConfig, error) {
	if port == "" {
		return nil, fmt.Errorf("No port provided.")
	}
	if path == "" || path == "/" {
		path = defaultMetricsPath
	}

	portNum, err := strconv.ParseUint(port, 10, 32)
	if err != nil {
		return nil, err
	}

	if len(protocol) == 0 {
		if portNum == 443 {
			protocol = "https"
		} else {
			protocol = "http"
		}
	}

	var whitelistedList []string
	if whitelisted != "" {
		whitelistedList = strings.Split(whitelisted, ",")
	}

	return &SourceConfig{
		Component:            component,
		Protocol:             protocol,
		Host:                 host,
		Port:                 uint(portNum),
		Path:                 path,
		AuthConfig:           auth,
		Whitelisted:          whitelistedList,
		WhitelistedLabelsMap: whitelistedLabelsMap,
		PodConfig:            podConfig,
		MetricsPrefix:        metricsPrefix,
	}, nil
}

// parseSourceConfig creates a new SourceConfig based on the provided flags.Uri instance.
func parseSourceConfig(uri flags.Uri, podId, namespaceId string) (*SourceConfig, error) {
	host, port, err := net.SplitHostPort(uri.Val.Host)
	if err != nil {
		return nil, err
	}

	component := uri.Key
	values := uri.Val.Query()
	protocol := uri.Val.Scheme
	path := uri.Val.Path
	whitelisted := values.Get("whitelisted")
	podIdLabel := values.Get("podIdLabel")
	namespaceIdLabel := values.Get("namespaceIdLabel")
	containerNameLabel := values.Get("containerNameLabel")
	metricsPrefix := values.Get("metricsPrefix")
	auth, err := parseAuthConfig(uri.Val)
	if err != nil {
		return nil, err
	}
	podConfig := NewPodConfig(podId, namespaceId, podIdLabel, namespaceIdLabel, containerNameLabel)

	whitelistedLabelsMap, err := parseWhitelistedLabels(values.Get("whitelistedLabels"))
	if err != nil {
		return nil, err
	}
	return newSourceConfig(component, protocol, host, port, path, *auth, whitelisted, metricsPrefix, podConfig, whitelistedLabelsMap)
}

// UpdateWhitelistedMetrics sets passed list as a list of whitelisted metrics.
func (config *SourceConfig) UpdateWhitelistedMetrics(list []string) {
	config.Whitelisted = list
}

// SourceConfigsFromFlags creates a slice of SourceConfig's base on the provided flags.
func SourceConfigsFromFlags(source flags.Uris, podId *string, namespaceId *string, defaultMetricsPrefix string) []*SourceConfig {
	var sourceConfigs []*SourceConfig
	for _, c := range source {
		if sourceConfig, err := parseSourceConfig(c, *podId, *namespaceId); err != nil {
			glog.Fatalf("Error while parsing source config flag %v: %v", c, err)
		} else {
			if sourceConfig.MetricsPrefix == "" {
				sourceConfig.MetricsPrefix = defaultMetricsPrefix
			}
			sourceConfigs = append(sourceConfigs, sourceConfig)
		}
	}
	return sourceConfigs
}

// parseWhitelistedLabels extracts the labels and their corresponding whitelisted values that will be filtered for.
func parseWhitelistedLabels(whitelistedLabels string) (map[string]map[string]bool, error) {
	// whitelistedLabels  is of the format whitelistedLabel1:val11,val12,etc.|whitelistedLabel2:val21
	labelsMap := make(map[string]map[string]bool)
	// URL.Query().Get() will return "" if whitelistedLabels is not specified
	if whitelistedLabels == "" {
		return labelsMap, nil
	}
	labelVals := strings.Split(whitelistedLabels, "|")
	for _, labelVal := range labelVals {
		labelAndValueParts := strings.Split(labelVal, ":")
		if len(labelAndValueParts) != 2 {
			return nil, fmt.Errorf("Incorrectly formatted whitelisted label and values: %v", labelVal)
		}
		labelKey := labelAndValueParts[0]
		if validWhitelistedLabels[labelKey] {
			labelsMap[labelKey] = make(map[string]bool)
			for _, val := range strings.Split(labelAndValueParts[1], ",") {
				labelsMap[labelAndValueParts[0]][val] = true
			}
		} else {
			return nil, fmt.Errorf("Filtering against label %v is unsupported. Only containerNameLabel, namespaceIdLabel, and podIdLabel are supported.", labelKey)
		}
	}
	return labelsMap, nil
}
<|endoftext|>


<commit_before>package gauja

import (
	"bytes"
	"strings"
)

type Message struct {
	Sender     string
	Command    string
	Parameters []string
}

func MakeMessage(command string, parameters ...string) Message {
	return Message{
		Sender:     "",
		Command:    command,
		Parameters: parameters,
	}
}

// Formatting: Message -> string

func (msg Message) String() string {
	var buffer bytes.Buffer
	if msg.Sender != "" {
		buffer.WriteString(":")
		buffer.WriteString(msg.Sender)
		buffer.WriteString(" ")
	}
	buffer.WriteString(msg.Command)
	n := len(msg.Parameters)
	if n > 0 {
		for _, parameter := range msg.Parameters[0 : n-1] {
			buffer.WriteString(" ")
			buffer.WriteString(parameter)
		}
		buffer.WriteString(" :")
		buffer.WriteString(msg.Parameters[n-1])
	}
	return buffer.String()
}

// Parsing: string -> Message

type parseState struct {
	input string
	pos   int
}

func ParseMessage(line string) Message {
	p := &parseState{
		input: line,
		pos:   0,
	}
	return Message{
		Sender:     p.parseSender(),
		Command:    p.parseCommand(),
		Parameters: p.parseParameters(),
	}
}

func (p *parseState) parseSender() string {
	if p.tryParseColon() {
		return p.parseWord()
	} else {
		return ""
	}
}

func (p *parseState) parseCommand() string {
	return p.parseWord()
}

func (p *parseState) parseParameters() []string {
	var result []string
	var i int = 0
	for p.hasInput() {
		if i >= 1000 {
			panic("Infinite loop in parseParameters")
		}
		i++
		if p.tryParseColon() {
			result = append(result, p.parseRest())
			break
		} else {
			result = append(result, p.parseWord())
		}
	}
	return result
}

func (p *parseState) tryParseColon() bool {
	if p.hasInput() && p.input[p.pos] == ':' {
		p.pos++
		return true
	} else {
		return false
	}
}

func (p *parseState) parseWord() string {
	end := strings.Index(p.input[p.pos:], " ")
	if end == -1 {
		result := p.input[p.pos:]
		p.pos = len(p.input)
		return result
	}
	result := p.input[p.pos : p.pos+end]
	p.pos = p.pos + end + 1
	return result
}

func (p *parseState) hasInput() bool {
	return p.pos < len(p.input)
}

func (p *parseState) parseRest() string {
	result := p.input[p.pos:]
	p.pos = len(p.input)
	return result
}
<commit_msg>Split message sender into nick, login, and host<commit_after>package gauja

import (
	"bytes"
	"strings"
)

type Message struct {
	Sender     Sender
	Command    string
	Parameters []string
}

type Sender struct {
	Nick  string
	Login string
	Host  string
}

func MakeMessage(command string, parameters ...string) Message {
	return Message{
		Sender:     Sender{},
		Command:    command,
		Parameters: parameters,
	}
}

// Formatting: Message -> string

func (msg Message) String() string {
	var buffer bytes.Buffer
	if msg.Sender.Nick != "" {
		buffer.WriteString(":")
		buffer.WriteString(msg.Sender.Nick)
		if msg.Sender.Login != "" {
			buffer.WriteString("!")
			buffer.WriteString(msg.Sender.Login)
		}
		if msg.Sender.Host != "" {
			buffer.WriteString("@")
			buffer.WriteString(msg.Sender.Host)
		}
		buffer.WriteString(" ")
	}
	buffer.WriteString(msg.Command)
	n := len(msg.Parameters)
	if n > 0 {
		for _, parameter := range msg.Parameters[0 : n-1] {
			buffer.WriteString(" ")
			buffer.WriteString(parameter)
		}
		buffer.WriteString(" :")
		buffer.WriteString(msg.Parameters[n-1])
	}
	return buffer.String()
}

// Parsing: string -> Message

type parseState struct {
	input string
	pos   int
}

func ParseMessage(line string) Message {
	p := &parseState{
		input: line,
		pos:   0,
	}
	return Message{
		Sender:     p.parseSender(),
		Command:    p.parseCommand(),
		Parameters: p.parseParameters(),
	}
}

func (p *parseState) parseSender() Sender {
	if p.tryParseColon() {
		sender := p.parseWord()
		nickAndLogin, host := splitOffOptionalSuffix(sender, "@")
		nick, login := splitOffOptionalSuffix(nickAndLogin, "!")
		return Sender{nick, login, host}
	}
	return Sender{}
}

func splitOffOptionalSuffix(s, del string) (string, string) {
	i := strings.Index(s, del)
	if i == -1 {
		return s, ""
	} else {
		return s[:i], s[i+1:]
	}
}

func (p *parseState) parseCommand() string {
	return p.parseWord()
}

func (p *parseState) parseParameters() []string {
	var result []string
	var i int = 0
	for p.hasInput() {
		if i >= 1000 {
			panic("Infinite loop in parseParameters")
		}
		i++
		if p.tryParseColon() {
			result = append(result, p.parseRest())
			break
		} else {
			result = append(result, p.parseWord())
		}
	}
	return result
}

func (p *parseState) tryParseColon() bool {
	if p.hasInput() && p.input[p.pos] == ':' {
		p.pos++
		return true
	} else {
		return false
	}
}

func (p *parseState) parseWord() string {
	end := strings.Index(p.input[p.pos:], " ")
	if end == -1 {
		result := p.input[p.pos:]
		p.pos = len(p.input)
		return result
	}
	result := p.input[p.pos : p.pos+end]
	p.pos = p.pos + end + 1
	return result
}

func (p *parseState) hasInput() bool {
	return p.pos < len(p.input)
}

func (p *parseState) parseRest() string {
	result := p.input[p.pos:]
	p.pos = len(p.input)
	return result
}
<|endoftext|>


<commit_before>package daemon

import (
	"context"
	"fmt"
	"sync"

	"github.com/spolu/warp"
	"github.com/spolu/warp/lib/logging"
)

// UserState represents the state of a user along with a list of all his
// sessions.
type UserState struct {
	token    string
	username string
	mode     warp.Mode
	sessions map[string]*Session
}

// User returns a warp.User from the current UserState.
func (u *UserState) User(
	ctx context.Context,
) warp.User {
	return warp.User{
		Token:    u.token,
		Username: u.username,
		Mode:     u.mode,
		Hosting:  false,
	}
}

// HostState represents the state of the host, in particular the host session,
// along with its UserState.
type HostState struct {
	UserState
	session *Session
}

// User returns a warp.User from the current HostState.
func (h *HostState) User(
	ctx context.Context,
) warp.User {
	return warp.User{
		Token:    h.UserState.token,
		Username: h.UserState.username,
		Mode:     h.UserState.mode,
		Hosting:  true,
	}
}

// Warp represents a pty served from a remote host attached to a token.
type Warp struct {
	token string

	windowSize warp.Size

	host         *HostState
	shellClients map[string]*UserState

	data chan []byte

	mutex *sync.Mutex
}

// State computes a warp.State from the current session. It acquires the session
// lock.
func (w *Warp) State(
	ctx context.Context,
) warp.State {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	state := warp.State{
		Warp:       w.token,
		WindowSize: w.windowSize,
		Users:      map[string]warp.User{},
	}

	state.Users[w.host.session.session.User] = w.host.User(ctx)

	for token, user := range w.shellClients {
		state.Users[token] = user.User(ctx)
	}

	return state
}

// Sessions return all connected sessions that are not the host session.
func (w *Warp) Sessions(
	ctx context.Context,
) []*Session {
	sessions := []*Session{}
	w.mutex.Lock()
	for _, user := range w.shellClients {
		for _, c := range user.sessions {
			sessions = append(sessions, c)
		}
	}
	// The host user's shell client sessions, if any.
	for _, c := range w.host.UserState.sessions {
		sessions = append(sessions, c)
	}
	w.mutex.Unlock()
	return sessions
}

// updateShellClients updates all shell clients with the current warp state.
func (w *Warp) updateShellClients(
	ctx context.Context,
) {
	st := w.State(ctx)
	sessions := w.Sessions(ctx)
	for _, ss := range sessions {
		logging.Logf(ctx,
			"Sending (client) state: session=%s cols=%d rows=%d",
			ss.ToString(), st.WindowSize.Rows, st.WindowSize.Cols,
		)

		ss.stateW.Encode(st)
	}
}

// updateHost updates the host with the current warp state.
func (w *Warp) updateHost(
	ctx context.Context,
) {
	st := w.State(ctx)

	logging.Logf(ctx,
		"Sending (host) state: session=%s cols=%d rows=%d",
		w.host.session.ToString(), st.WindowSize.Rows, st.WindowSize.Cols,
	)

	w.host.session.stateW.Encode(st)
}

// rcvClientData handles incoming client data and commits it to the data
// channel if the client is authorized to do so.
func (w *Warp) rcvClientData(
	ctx context.Context,
	ss *Session,
	data []byte,
) {
	var mode warp.Mode
	w.mutex.Lock()
	mode = w.shellClients[ss.session.User].mode
	w.mutex.Unlock()

	if mode&warp.ModeShellWrite != 0 {
		w.data <- data
	}
}

func (w *Warp) rcvHostData(
	ctx context.Context,
	ss *Session,
	data []byte,
) {
	sessions := w.Sessions(ctx)
	for _, s := range sessions {
		logging.Logf(ctx,
			"Sending data to session: session=%s size=%d",
			s.ToString(), len(data),
		)
		_, err := s.dataC.Write(data)
		if err != nil {
			s.SendError(ctx,
				"data_send_failed",
				fmt.Sprintf("Error sending data: %v", err),
			)
			// This will disconnect the client and clean it up from the
			// session
			s.cancel()
		}
	}
}

func (w *Warp) handleHost(
	ctx context.Context,
	ss *Session,
) error {
	// run state updates
	go func() {
	HOSTLOOP:
		for {
			var st warp.HostUpdate
			if err := w.host.session.updateR.Decode(&st); err != nil {
				ss.SendError(ctx,
					"invalid_host_update",
					fmt.Sprintf("Host update decoding failed: %v", err),
				)
				break HOSTLOOP
			}

			// Check that the warp token is the same.
			if st.Warp != w.token {
				ss.SendError(ctx,
					"invalid_host_update",
					fmt.Sprintf(
						"Host update warp mismatch: %s", st.Warp,
					),
				)
				break HOSTLOOP
			}

			// Check that the session is the same in particular the secret to
			// protect against spoofing attempts.
			if st.From.Token != ss.session.Token ||
				st.From.User != ss.session.User ||
				st.From.Secret != ss.session.Secret {
				ss.SendError(ctx,
					"invalid_host_update",
					fmt.Sprintf(
						"Host update host mismatch: %s", st.From.Token,
					),
				)
				break HOSTLOOP
			}

			for token, _ := range st.Modes {
				_, ok := w.shellClients[token]
				if !ok {
					ss.SendError(ctx,
						"invalid_host_update",
						fmt.Sprintf(
							"Host update unknown client: %s", token,
						),
					)
					break HOSTLOOP
				}
			}

			w.mutex.Lock()
			w.windowSize = st.WindowSize
			for token, mode := range st.Modes {
				w.shellClients[token].mode = mode
			}
			w.mutex.Unlock()

			logging.Logf(ctx,
				"Received host update: session=%s cols=%d rows=%d",
				ss.ToString(), st.WindowSize.Rows, st.WindowSize.Cols,
			)

			w.updateShellClients(ctx)
		}
		ss.cancel()
	}()

	// Receive host data.
	go func() {
		buf := make([]byte, 1024)
		for {
			nr, err := ss.dataC.Read(buf)
			if nr > 0 {
				cpy := make([]byte, nr)
				copy(cpy, buf)

				logging.Logf(ctx,
					"Received data from host: session=%s size=%d",
					ss.ToString(), nr,
				)
				w.rcvHostData(ctx, ss, cpy)
			}
			if err != nil {
				ss.SendError(ctx,
					"data_receive_failed",
					fmt.Sprintf("Error receiving data: %v", err),
				)
				break
			}
			select {
			case <-ss.ctx.Done():
				break
			default:
			}
		}
		ss.cancel()
	}()

	// Send data to host.
	go func() {
		for {
			select {
			case buf := <-w.data:

				logging.Logf(ctx,
					"Sending data to host: session=%s size=%d",
					ss.ToString(), len(buf),
				)

				_, err := ss.dataC.Write(buf)
				if err != nil {
					ss.SendError(ctx,
						"data_send_failed",
						fmt.Sprintf("Error sending data: %v", err),
					)
					break
				}
			case <-ss.ctx.Done():
				break
			default:
			}
		}
		ss.cancel()
	}()

	logging.Logf(ctx,
		"Host session running: session=%s",
		ss.ToString(),
	)

	<-ss.ctx.Done()

	// Cancel all clients.
	logging.Logf(ctx,
		"Cancelling all clients: session=%s",
		ss.ToString(),
	)
	sessions := w.Sessions(ctx)
	for _, s := range sessions {
		s.cancel()
	}

	return nil
}

func (w *Warp) handleClient(
	ctx context.Context,
	ss *Session,
) error {
	// Add the client.
	w.mutex.Lock()
	isHostSession := false
	if ss.session.User == w.host.UserState.token {
		isHostSession = true
		// If we have a session conflict, let's kill the old one.
		if s, ok := w.host.UserState.sessions[ss.session.Token]; ok {
			s.cancel()
		}
		w.host.UserState.sessions[ss.session.Token] = ss
	} else {
		if _, ok := w.shellClients[ss.session.User]; !ok {
			w.shellClients[ss.session.User] = &UserState{
				token:    ss.session.User,
				username: ss.username,
				mode:     warp.ModeShellRead,
				sessions: map[string]*Session{},
			}
		}
		// If we have a session conflict, let's kill the old one.
		if s, ok := w.shellClients[ss.session.User].sessions[ss.session.Token]; ok {
			s.cancel()
		}
		w.shellClients[ss.session.User].sessions[ss.session.Token] = ss
	}
	w.mutex.Unlock()

	// Receive client data.
	go func() {
		buf := make([]byte, 1024)
		for {
			nr, err := ss.dataC.Read(buf)
			if nr > 0 {
				cpy := make([]byte, nr)
				copy(cpy, buf)

				logging.Logf(ctx,
					"Received data from client: session=%s size=%d",
					ss.ToString(), nr,
				)
				w.rcvClientData(ctx, ss, cpy)
			}
			if err != nil {
				ss.SendError(ctx,
					"data_receive_failed",
					fmt.Sprintf("Error receiving data: %v", err),
				)
				break
			}
			select {
			case <-ss.ctx.Done():
				break
			default:
			}
		}
		ss.cancel()
	}()

	// Send initial state.
	st := w.State(ctx)
	logging.Logf(ctx,
		"Sending initial state: session=%s cols=%d rows=%d",
		ss.ToString(), st.WindowSize.Rows, st.WindowSize.Cols,
	)
	ss.stateW.Encode(st)

	// Update host and clients.
	w.updateHost(ctx)
	w.updateShellClients(ctx)

	logging.Logf(ctx,
		"Client session running: session=%s",
		ss.ToString(),
	)

	<-ss.ctx.Done()

	// Clean-up client.
	logging.Logf(ctx,
		"Cleaning-up client: session=%s",
		ss.ToString(),
	)
	w.mutex.Lock()
	if isHostSession {
		delete(w.host.sessions, ss.session.Token)
	} else {
		delete(w.shellClients[ss.session.User].sessions, ss.session.Token)
		if len(w.shellClients[ss.session.User].sessions) == 0 {
			delete(w.shellClients, ss.session.User)
		}
	}
	w.mutex.Unlock()

	// Update host and remaining clients
	w.updateHost(ctx)
	w.updateShellClients(ctx)

	return nil
}
<commit_msg>Do not crash if host user list is stale on host update<commit_after>package daemon

import (
	"context"
	"fmt"
	"sync"

	"github.com/spolu/warp"
	"github.com/spolu/warp/lib/logging"
)

// UserState represents the state of a user along with a list of all his
// sessions.
type UserState struct {
	token    string
	username string
	mode     warp.Mode
	sessions map[string]*Session
}

// User returns a warp.User from the current UserState.
func (u *UserState) User(
	ctx context.Context,
) warp.User {
	return warp.User{
		Token:    u.token,
		Username: u.username,
		Mode:     u.mode,
		Hosting:  false,
	}
}

// HostState represents the state of the host, in particular the host session,
// along with its UserState.
type HostState struct {
	UserState
	session *Session
}

// User returns a warp.User from the current HostState.
func (h *HostState) User(
	ctx context.Context,
) warp.User {
	return warp.User{
		Token:    h.UserState.token,
		Username: h.UserState.username,
		Mode:     h.UserState.mode,
		Hosting:  true,
	}
}

// Warp represents a pty served from a remote host attached to a token.
type Warp struct {
	token string

	windowSize warp.Size

	host         *HostState
	shellClients map[string]*UserState

	data chan []byte

	mutex *sync.Mutex
}

// State computes a warp.State from the current session. It acquires the session
// lock.
func (w *Warp) State(
	ctx context.Context,
) warp.State {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	state := warp.State{
		Warp:       w.token,
		WindowSize: w.windowSize,
		Users:      map[string]warp.User{},
	}

	state.Users[w.host.session.session.User] = w.host.User(ctx)

	for token, user := range w.shellClients {
		state.Users[token] = user.User(ctx)
	}

	return state
}

// Sessions return all connected sessions that are not the host session.
func (w *Warp) Sessions(
	ctx context.Context,
) []*Session {
	sessions := []*Session{}
	w.mutex.Lock()
	for _, user := range w.shellClients {
		for _, c := range user.sessions {
			sessions = append(sessions, c)
		}
	}
	// The host user's shell client sessions, if any.
	for _, c := range w.host.UserState.sessions {
		sessions = append(sessions, c)
	}
	w.mutex.Unlock()
	return sessions
}

// updateShellClients updates all shell clients with the current warp state.
func (w *Warp) updateShellClients(
	ctx context.Context,
) {
	st := w.State(ctx)
	sessions := w.Sessions(ctx)
	for _, ss := range sessions {
		logging.Logf(ctx,
			"Sending (client) state: session=%s cols=%d rows=%d",
			ss.ToString(), st.WindowSize.Rows, st.WindowSize.Cols,
		)

		ss.stateW.Encode(st)
	}
}

// updateHost updates the host with the current warp state.
func (w *Warp) updateHost(
	ctx context.Context,
) {
	st := w.State(ctx)

	logging.Logf(ctx,
		"Sending (host) state: session=%s cols=%d rows=%d",
		w.host.session.ToString(), st.WindowSize.Rows, st.WindowSize.Cols,
	)

	w.host.session.stateW.Encode(st)
}

// rcvClientData handles incoming client data and commits it to the data
// channel if the client is authorized to do so.
func (w *Warp) rcvClientData(
	ctx context.Context,
	ss *Session,
	data []byte,
) {
	var mode warp.Mode
	w.mutex.Lock()
	mode = w.shellClients[ss.session.User].mode
	w.mutex.Unlock()

	if mode&warp.ModeShellWrite != 0 {
		w.data <- data
	}
}

func (w *Warp) rcvHostData(
	ctx context.Context,
	ss *Session,
	data []byte,
) {
	sessions := w.Sessions(ctx)
	for _, s := range sessions {
		logging.Logf(ctx,
			"Sending data to session: session=%s size=%d",
			s.ToString(), len(data),
		)
		_, err := s.dataC.Write(data)
		if err != nil {
			s.SendError(ctx,
				"data_send_failed",
				fmt.Sprintf("Error sending data: %v", err),
			)
			// This will disconnect the client and clean it up from the
			// session
			s.cancel()
		}
	}
}

func (w *Warp) handleHost(
	ctx context.Context,
	ss *Session,
) error {
	// run state updates
	go func() {
	HOSTLOOP:
		for {
			var st warp.HostUpdate
			if err := w.host.session.updateR.Decode(&st); err != nil {
				ss.SendError(ctx,
					"invalid_host_update",
					fmt.Sprintf("Host update decoding failed: %v", err),
				)
				break HOSTLOOP
			}

			// Check that the warp token is the same.
			if st.Warp != w.token {
				ss.SendError(ctx,
					"invalid_host_update",
					fmt.Sprintf(
						"Host update warp mismatch: %s", st.Warp,
					),
				)
				break HOSTLOOP
			}

			// Check that the session is the same in particular the secret to
			// protect against spoofing attempts.
			if st.From.Token != ss.session.Token ||
				st.From.User != ss.session.User ||
				st.From.Secret != ss.session.Secret {
				ss.SendError(ctx,
					"invalid_host_update",
					fmt.Sprintf(
						"Host update host mismatch: %s", st.From.Token,
					),
				)
				break HOSTLOOP
			}

			w.mutex.Lock()
			w.windowSize = st.WindowSize
			for user, mode := range st.Modes {
				if _, ok := w.shellClients[user]; ok {
					w.shellClients[user].mode = mode
				} else {
					logging.Logf(ctx,
						"Unknown user from host update: session=%s user=%s",
						ss.ToString(), user,
					)
				}
			}
			w.mutex.Unlock()

			logging.Logf(ctx,
				"Received host update: session=%s cols=%d rows=%d",
				ss.ToString(), st.WindowSize.Rows, st.WindowSize.Cols,
			)

			w.updateShellClients(ctx)
		}
		ss.cancel()
	}()

	// Receive host data.
	go func() {
		buf := make([]byte, 1024)
		for {
			nr, err := ss.dataC.Read(buf)
			if nr > 0 {
				cpy := make([]byte, nr)
				copy(cpy, buf)

				logging.Logf(ctx,
					"Received data from host: session=%s size=%d",
					ss.ToString(), nr,
				)
				w.rcvHostData(ctx, ss, cpy)
			}
			if err != nil {
				ss.SendError(ctx,
					"data_receive_failed",
					fmt.Sprintf("Error receiving data: %v", err),
				)
				break
			}
			select {
			case <-ss.ctx.Done():
				break
			default:
			}
		}
		ss.cancel()
	}()

	// Send data to host.
	go func() {
		for {
			select {
			case buf := <-w.data:

				logging.Logf(ctx,
					"Sending data to host: session=%s size=%d",
					ss.ToString(), len(buf),
				)

				_, err := ss.dataC.Write(buf)
				if err != nil {
					ss.SendError(ctx,
						"data_send_failed",
						fmt.Sprintf("Error sending data: %v", err),
					)
					break
				}
			case <-ss.ctx.Done():
				break
			default:
			}
		}
		ss.cancel()
	}()

	logging.Logf(ctx,
		"Host session running: session=%s",
		ss.ToString(),
	)

	<-ss.ctx.Done()

	// Cancel all clients.
	logging.Logf(ctx,
		"Cancelling all clients: session=%s",
		ss.ToString(),
	)
	sessions := w.Sessions(ctx)
	for _, s := range sessions {
		s.cancel()
	}

	return nil
}

func (w *Warp) handleClient(
	ctx context.Context,
	ss *Session,
) error {
	// Add the client.
	w.mutex.Lock()
	isHostSession := false
	if ss.session.User == w.host.UserState.token {
		isHostSession = true
		// If we have a session conflict, let's kill the old one.
		if s, ok := w.host.UserState.sessions[ss.session.Token]; ok {
			s.cancel()
		}
		w.host.UserState.sessions[ss.session.Token] = ss
	} else {
		if _, ok := w.shellClients[ss.session.User]; !ok {
			w.shellClients[ss.session.User] = &UserState{
				token:    ss.session.User,
				username: ss.username,
				mode:     warp.ModeShellRead,
				sessions: map[string]*Session{},
			}
		}
		// If we have a session conflict, let's kill the old one.
		if s, ok := w.shellClients[ss.session.User].sessions[ss.session.Token]; ok {
			s.cancel()
		}
		w.shellClients[ss.session.User].sessions[ss.session.Token] = ss
	}
	w.mutex.Unlock()

	// Receive client data.
	go func() {
		buf := make([]byte, 1024)
		for {
			nr, err := ss.dataC.Read(buf)
			if nr > 0 {
				cpy := make([]byte, nr)
				copy(cpy, buf)

				logging.Logf(ctx,
					"Received data from client: session=%s size=%d",
					ss.ToString(), nr,
				)
				w.rcvClientData(ctx, ss, cpy)
			}
			if err != nil {
				ss.SendError(ctx,
					"data_receive_failed",
					fmt.Sprintf("Error receiving data: %v", err),
				)
				break
			}
			select {
			case <-ss.ctx.Done():
				break
			default:
			}
		}
		ss.cancel()
	}()

	// Send initial state.
	st := w.State(ctx)
	logging.Logf(ctx,
		"Sending initial state: session=%s cols=%d rows=%d",
		ss.ToString(), st.WindowSize.Rows, st.WindowSize.Cols,
	)
	ss.stateW.Encode(st)

	// Update host and clients.
	w.updateHost(ctx)
	w.updateShellClients(ctx)

	logging.Logf(ctx,
		"Client session running: session=%s",
		ss.ToString(),
	)

	<-ss.ctx.Done()

	// Clean-up client.
	logging.Logf(ctx,
		"Cleaning-up client: session=%s",
		ss.ToString(),
	)
	w.mutex.Lock()
	if isHostSession {
		delete(w.host.sessions, ss.session.Token)
	} else {
		delete(w.shellClients[ss.session.User].sessions, ss.session.Token)
		if len(w.shellClients[ss.session.User].sessions) == 0 {
			delete(w.shellClients, ss.session.User)
		}
	}
	w.mutex.Unlock()

	// Update host and remaining clients
	w.updateHost(ctx)
	w.updateShellClients(ctx)

	return nil
}
<|endoftext|>