<commit_before>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package commands

import (
	. "github.com/limetext/lime/backend"
	"testing"
)

func TestNewWindow(t *testing.T) {
	ed := GetEditor()
	l := len(ed.Windows())
	ed.CommandHandler().RunWindowCommand(ed.ActiveWindow(), "new_window", nil)

	if len(ed.Windows()) != l+1 {
		t.Errorf("Expected %d window, but got %d", l+1, len(ed.Windows()))
	}
}

func TestCloseAll(t *testing.T) {
	ed := GetEditor()

	w := ed.NewWindow()
	defer w.Close()

	l := len(w.Views())

	ed.CommandHandler().RunWindowCommand(w, "new_file", nil)
	ed.CommandHandler().RunWindowCommand(w, "new_file", nil)
	ed.CommandHandler().RunWindowCommand(w, "new_file", nil)

	ed.CommandHandler().RunWindowCommand(w, "close_all", nil)

	if len(w.Views()) != l {
		t.Errorf("Expected %d views, but got %d", l, len(w.Views()))
	}
}

func TestCloseWindow(t *testing.T) {
	ed := GetEditor()
	w := ed.NewWindow()
	l := len(ed.Windows())
	ed.CommandHandler().RunWindowCommand(w, "close_window", nil)

	if len(ed.Windows()) != l-1 {
		t.Errorf("Expected %d window, but got %d", l-1, len(ed.Windows()))
	}
}

func TestNewAppWindow(t *testing.T) {
	ed := GetEditor()
	l := len(ed.Windows())
	ed.CommandHandler().RunApplicationCommand("new_window", nil)

	if len(ed.Windows()) != l+1 {
		t.Errorf("Expected %d window, but got %d", l+1, len(ed.Windows()))
	}
}

func TestCloseAppWindow(t *testing.T) {
	ed := GetEditor()
	_ = ed.NewWindow()
	l := len(ed.Windows())
	ed.CommandHandler().RunApplicationCommand("close_window", nil)

	if len(ed.Windows()) != l-1 {
		t.Errorf("Expected %d window, but got %d", l-1, len(ed.Windows()))
	}
}
<commit_msg>Make sure the test checks that all the views are closed.<commit_after>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package commands

import (
	. "github.com/limetext/lime/backend"
	"testing"
)

func TestNewWindow(t *testing.T) {
	ed := GetEditor()
	l := len(ed.Windows())
	ed.CommandHandler().RunWindowCommand(ed.ActiveWindow(), "new_window", nil)

	if len(ed.Windows()) != l+1 {
		t.Errorf("Expected %d window, but got %d", l+1, len(ed.Windows()))
	}
}

func TestCloseAll(t *testing.T) {
	ed := GetEditor()

	w := ed.NewWindow()
	defer w.Close()

	ed.CommandHandler().RunWindowCommand(w, "new_file", nil)
	ed.CommandHandler().RunWindowCommand(w, "new_file", nil)
	ed.CommandHandler().RunWindowCommand(w, "new_file", nil)

	ed.CommandHandler().RunWindowCommand(w, "close_all", nil)

	if len(w.Views()) != 0 {
		t.Errorf("Expected no views, but got %d", len(w.Views()))
	}
}

func TestCloseWindow(t *testing.T) {
	ed := GetEditor()
	w := ed.NewWindow()
	l := len(ed.Windows())
	ed.CommandHandler().RunWindowCommand(w, "close_window", nil)

	if len(ed.Windows()) != l-1 {
		t.Errorf("Expected %d window, but got %d", l-1, len(ed.Windows()))
	}
}

func TestNewAppWindow(t *testing.T) {
	ed := GetEditor()
	l := len(ed.Windows())
	ed.CommandHandler().RunApplicationCommand("new_window", nil)

	if len(ed.Windows()) != l+1 {
		t.Errorf("Expected %d window, but got %d", l+1, len(ed.Windows()))
	}
}

func TestCloseAppWindow(t *testing.T) {
	ed := GetEditor()
	_ = ed.NewWindow()
	l := len(ed.Windows())
	ed.CommandHandler().RunApplicationCommand("close_window", nil)

	if len(ed.Windows()) != l-1 {
		t.Errorf("Expected %d window, but got %d", l-1, len(ed.Windows()))
	}
}
<|endoftext|>


<commit_before>package schedops

import (
	"fmt"
	"regexp"
	"strings"
	"time"

	"github.com/libopenstorage/openstorage/api"
	"github.com/portworx/torpedo/drivers/node"
	"github.com/portworx/torpedo/drivers/scheduler"
	"github.com/portworx/torpedo/drivers/volume"
	"github.com/portworx/torpedo/pkg/errors"
	"github.com/portworx/torpedo/pkg/k8sops"
	"github.com/portworx/torpedo/pkg/task"
)

const (
	// k8sPxRunningLabelKey is the label key used for px state
	k8sPxRunningLabelKey = "px/enabled"
	// k8sPxNotRunningLabelValue is label value for a not running px state
	k8sPxNotRunningLabelValue = "false"
	// k8sPodsRootDir is the directory under which k8s keeps all pods data
	k8sPodsRootDir = "/var/lib/kubelet/pods"
)

type k8sSchedOps struct{}

func (k *k8sSchedOps) DisableOnNode(n node.Node) error {
	return k8sops.Instance().AddLabelOnNode(n.Name, k8sPxRunningLabelKey, k8sPxNotRunningLabelValue)
}

func (k *k8sSchedOps) ValidateOnNode(n node.Node) error {
	return &errors.ErrNotSupported{
		Type:      "Function",
		Operation: "ValidateOnNode",
	}
}

func (k *k8sSchedOps) EnableOnNode(n node.Node) error {
	return k8sops.Instance().RemoveLabelOnNode(n.Name, k8sPxRunningLabelKey)
}

func (k *k8sSchedOps) ValidateAddLabels(replicaNodes []api.Node, vol *api.Volume) error {
	pvc, ok := vol.Locator.VolumeLabels["pvc"]
	if !ok {
		return nil
	}

	var missingLabelNodes []string
	for _, rs := range replicaNodes {
		t := func() (interface{}, error) {
			n, err := k8sops.Instance().GetNodeByName(rs.Id)
			if err == nil && n != nil {
				return n.Labels, nil
			}

			addrs := []string{rs.DataIp, rs.MgmtIp}
			n, err = k8sops.Instance().SearchNodeByAddresses(addrs)
			if err == nil && n != nil {
				return n.Labels, nil
			}

			return nil, fmt.Errorf("failed to locate node using id: %s and addresses: %v", rs.Id, addrs)
		}

		ret, err := task.DoRetryWithTimeout(t, 1*time.Minute, 5*time.Second)
		if err != nil {
			return err
		}
		nodeLabels := ret.(map[string]string)
		if _, ok := nodeLabels[pvc]; !ok {
			missingLabelNodes = append(missingLabelNodes, rs.Id)
		}
	}

	if len(missingLabelNodes) > 0 {
		return &ErrLabelMissingOnNode{
			Label: pvc,
			Nodes: missingLabelNodes,
		}
	}
	return nil
}

func (k *k8sSchedOps) ValidateRemoveLabels(vol *volume.Volume, sched scheduler.Driver) error {
	pvcLabel := vol.Name
	var staleLabelNodes []string
	for _, n := range sched.GetNodes() {
		if n.Type == node.TypeWorker {
			t := func() (interface{}, error) {
				return k8sops.Instance().GetLabelsOnNode(n.Name)
			}
			ret, err := task.DoRetryWithTimeout(t, 1*time.Minute, 5*time.Second)
			if err != nil {
				return err
			}
			nodeLabels := ret.(map[string]string)
			if _, ok := nodeLabels[pvcLabel]; ok {
				staleLabelNodes = append(staleLabelNodes, n.Name)
			}
		}
	}

	if len(staleLabelNodes) > 0 {
		return &ErrLabelNotRemovedFromNode{
			Label: pvcLabel,
			Nodes: staleLabelNodes,
		}
	}

	return nil
}

func (k *k8sSchedOps) GetVolumeName(vol *volume.Volume) string {
	if vol != nil && vol.ID != "" {
		return fmt.Sprintf("pvc-%s", vol.ID)
	}
	return ""
}

func (k *k8sSchedOps) ValidateVolumeCleanup(sched scheduler.Driver, d node.Driver) error {
	nodeToPodsMap := make(map[string][]string)
	nodeMap := make(map[string]node.Node)

	connOpts := node.ConnectionOpts{
		Timeout:         1 * time.Minute,
		TimeBeforeRetry: 10 * time.Second,
	}
	listVolOpts := node.FindOpts{
		ConnectionOpts: connOpts,
		Name:           "*portworx-volume",
	}

	for _, n := range sched.GetNodes() {
		if n.Type == node.TypeWorker {
			volDirList, _ := d.FindFiles(k8sPodsRootDir, n, listVolOpts)
			nodeToPodsMap[n.Name] = separateFilePaths(volDirList)
			nodeMap[n.Name] = n
		}
	}

	existingPods, _ := k8sops.Instance().GetPods("")

	orphanPodsMap := make(map[string][]string)
	dirtyVolPodsMap := make(map[string][]string)

	for nodeName, volDirPaths := range nodeToPodsMap {
		var orphanPods []string
		var dirtyVolPods []string

		for _, path := range volDirPaths {
			podUID := extractPodUID(path)
			found := false
			for _, existingPod := range existingPods.Items {
				if podUID == string(existingPod.UID) {
					found = true
					break
				}
			}
			if found {
				continue
			}
			orphanPods = append(orphanPods, podUID)

			// Check if there are files under portworx volume
			// We use a depth of 2 because the files stored in the volume are in the pvc
			// directory under the portworx-volume folder for that pod. For instance,
			// ../kubernetes-io~portworx-volume/pvc-<id>/<all_user_files>
			n := nodeMap[nodeName]
			findFileOpts := node.FindOpts{
				ConnectionOpts: connOpts,
				MinDepth:       2,
				MaxDepth:       2,
			}
			files, _ := d.FindFiles(path, n, findFileOpts)
			if len(strings.TrimSpace(files)) > 0 {
				dirtyVolPods = append(dirtyVolPods, podUID)
			}
		}

		if len(orphanPods) > 0 {
			orphanPodsMap[nodeName] = orphanPods
			if len(dirtyVolPods) > 0 {
				dirtyVolPodsMap[nodeName] = dirtyVolPods
			}
		}
	}

	if len(orphanPodsMap) == 0 {
		return nil
	}
	return &ErrFailedToCleanupVolume{
		OrphanPods:   orphanPodsMap,
		DirtyVolPods: dirtyVolPodsMap,
	}
}

func separateFilePaths(volDirList string) []string {
	trimmedList := strings.TrimSpace(volDirList)
	if trimmedList == "" {
		return []string{}
	}
	return strings.Split(trimmedList, "\n")
}

func extractPodUID(volDirPath string) string {
	re := regexp.MustCompile(k8sPodsRootDir +
		"/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/.*")
	match := re.FindStringSubmatch(volDirPath)
	if len(match) > 1 {
		return match[1]
	}
	return ""
}

func init() {
	k := &k8sSchedOps{}
	Register("k8s", k)
}
<commit_msg>increase timeout to check for removal of volume labels<commit_after>package schedops

import (
	"fmt"
	"regexp"
	"strings"
	"time"

	"github.com/libopenstorage/openstorage/api"
	"github.com/portworx/torpedo/drivers/node"
	"github.com/portworx/torpedo/drivers/scheduler"
	"github.com/portworx/torpedo/drivers/volume"
	"github.com/portworx/torpedo/pkg/errors"
	"github.com/portworx/torpedo/pkg/k8sops"
	"github.com/portworx/torpedo/pkg/task"
)

const (
	// k8sPxRunningLabelKey is the label key used for px state
	k8sPxRunningLabelKey = "px/enabled"
	// k8sPxNotRunningLabelValue is label value for a not running px state
	k8sPxNotRunningLabelValue = "false"
	// k8sPodsRootDir is the directory under which k8s keeps all pods data
	k8sPodsRootDir = "/var/lib/kubelet/pods"
)

type k8sSchedOps struct{}

func (k *k8sSchedOps) DisableOnNode(n node.Node) error {
	return k8sops.Instance().AddLabelOnNode(n.Name, k8sPxRunningLabelKey, k8sPxNotRunningLabelValue)
}

func (k *k8sSchedOps) ValidateOnNode(n node.Node) error {
	return &errors.ErrNotSupported{
		Type:      "Function",
		Operation: "ValidateOnNode",
	}
}

func (k *k8sSchedOps) EnableOnNode(n node.Node) error {
	return k8sops.Instance().RemoveLabelOnNode(n.Name, k8sPxRunningLabelKey)
}

func (k *k8sSchedOps) ValidateAddLabels(replicaNodes []api.Node, vol *api.Volume) error {
	pvc, ok := vol.Locator.VolumeLabels["pvc"]
	if !ok {
		return nil
	}

	var missingLabelNodes []string
	for _, rs := range replicaNodes {
		t := func() (interface{}, error) {
			n, err := k8sops.Instance().GetNodeByName(rs.Id)
			if err == nil && n != nil {
				return n.Labels, nil
			}

			addrs := []string{rs.DataIp, rs.MgmtIp}
			n, err = k8sops.Instance().SearchNodeByAddresses(addrs)
			if err == nil && n != nil {
				return n.Labels, nil
			}

			return nil, fmt.Errorf("failed to locate node using id: %s and addresses: %v", rs.Id, addrs)
		}

		ret, err := task.DoRetryWithTimeout(t, 1*time.Minute, 5*time.Second)
		if err != nil {
			return err
		}
		nodeLabels := ret.(map[string]string)
		if _, ok := nodeLabels[pvc]; !ok {
			missingLabelNodes = append(missingLabelNodes, rs.Id)
		}
	}

	if len(missingLabelNodes) > 0 {
		return &ErrLabelMissingOnNode{
			Label: pvc,
			Nodes: missingLabelNodes,
		}
	}
	return nil
}

func (k *k8sSchedOps) ValidateRemoveLabels(vol *volume.Volume, sched scheduler.Driver) error {
	pvcLabel := vol.Name
	var staleLabelNodes []string
	for _, n := range sched.GetNodes() {
		if n.Type == node.TypeWorker {
			t := func() (interface{}, error) {
				return k8sops.Instance().GetLabelsOnNode(n.Name)
			}
			ret, err := task.DoRetryWithTimeout(t, 5*time.Minute, 5*time.Second)
			if err != nil {
				return err
			}
			nodeLabels := ret.(map[string]string)
			if _, ok := nodeLabels[pvcLabel]; ok {
				staleLabelNodes = append(staleLabelNodes, n.Name)
			}
		}
	}

	if len(staleLabelNodes) > 0 {
		return &ErrLabelNotRemovedFromNode{
			Label: pvcLabel,
			Nodes: staleLabelNodes,
		}
	}

	return nil
}

func (k *k8sSchedOps) GetVolumeName(vol *volume.Volume) string {
	if vol != nil && vol.ID != "" {
		return fmt.Sprintf("pvc-%s", vol.ID)
	}
	return ""
}

func (k *k8sSchedOps) ValidateVolumeCleanup(sched scheduler.Driver, d node.Driver) error {
	nodeToPodsMap := make(map[string][]string)
	nodeMap := make(map[string]node.Node)

	connOpts := node.ConnectionOpts{
		Timeout:         1 * time.Minute,
		TimeBeforeRetry: 10 * time.Second,
	}
	listVolOpts := node.FindOpts{
		ConnectionOpts: connOpts,
		Name:           "*portworx-volume",
	}

	for _, n := range sched.GetNodes() {
		if n.Type == node.TypeWorker {
			volDirList, _ := d.FindFiles(k8sPodsRootDir, n, listVolOpts)
			nodeToPodsMap[n.Name] = separateFilePaths(volDirList)
			nodeMap[n.Name] = n
		}
	}

	existingPods, _ := k8sops.Instance().GetPods("")

	orphanPodsMap := make(map[string][]string)
	dirtyVolPodsMap := make(map[string][]string)

	for nodeName, volDirPaths := range nodeToPodsMap {
		var orphanPods []string
		var dirtyVolPods []string

		for _, path := range volDirPaths {
			podUID := extractPodUID(path)
			found := false
			for _, existingPod := range existingPods.Items {
				if podUID == string(existingPod.UID) {
					found = true
					break
				}
			}
			if found {
				continue
			}
			orphanPods = append(orphanPods, podUID)

			// Check if there are files under portworx volume
			// We use a depth of 2 because the files stored in the volume are in the pvc
			// directory under the portworx-volume folder for that pod. For instance,
			// ../kubernetes-io~portworx-volume/pvc-<id>/<all_user_files>
			n := nodeMap[nodeName]
			findFileOpts := node.FindOpts{
				ConnectionOpts: connOpts,
				MinDepth:       2,
				MaxDepth:       2,
			}
			files, _ := d.FindFiles(path, n, findFileOpts)
			if len(strings.TrimSpace(files)) > 0 {
				dirtyVolPods = append(dirtyVolPods, podUID)
			}
		}

		if len(orphanPods) > 0 {
			orphanPodsMap[nodeName] = orphanPods
			if len(dirtyVolPods) > 0 {
				dirtyVolPodsMap[nodeName] = dirtyVolPods
			}
		}
	}

	if len(orphanPodsMap) == 0 {
		return nil
	}
	return &ErrFailedToCleanupVolume{
		OrphanPods:   orphanPodsMap,
		DirtyVolPods: dirtyVolPodsMap,
	}
}

func separateFilePaths(volDirList string) []string {
	trimmedList := strings.TrimSpace(volDirList)
	if trimmedList == "" {
		return []string{}
	}
	return strings.Split(trimmedList, "\n")
}

func extractPodUID(volDirPath string) string {
	re := regexp.MustCompile(k8sPodsRootDir +
		"/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/.*")
	match := re.FindStringSubmatch(volDirPath)
	if len(match) > 1 {
		return match[1]
	}
	return ""
}

func init() {
	k := &k8sSchedOps{}
	Register("k8s", k)
}
<|endoftext|>


<commit_before>package linebot

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strconv"
	"time"
)

// API URLs
const (
	EndPoint          = "https://api.line.me"
	PushMessage       = "/v2/bot/message/push"
	ReplyMessage      = "/v2/bot/message/reply"
	GetMessageContent = "/v2/bot/message/%s/content"
	LeaveGroup        = "/v2/bot/group/%s/leave"
	LeaveRoom         = "/v2/bot/room/%s/leave"
	GetProfile        = "/v2/bot/profile/%s"
)

// APISendResult ...
type APISendResult struct {
	Message string `json:"message"`
}

// BasicResponse ...
type BasicResponse struct {
}

// MessageContentResponse ...
type MessageContentResponse struct {
	Content       io.ReadCloser
	ContentLength int64
	ContentType   string
}

// UserProfileResponse ...
type UserProfileResponse struct {
	UserID        string `json:"userId"`
	DisplayName   string `json:"displayName"`
	PictureURL    string `json:"pictureUrl"`
	StatusMessage string `json:"statusMessage"`
}

// Client ...
type Client struct {
	endPoint           string
	channelAccessToken string
}

var eventHandler EventHandler
var channelSecret string

// NewClient ...
func NewClient(channelAccessToken string) *Client {
	return &Client{
		channelAccessToken: channelAccessToken,
		endPoint:           EndPoint,
	}
}

// SetEventHandler ...
func (c *Client) SetEventHandler(event EventHandler) {
	eventHandler = event
}

// SetChannelSecret ...
func (c *Client) SetChannelSecret(secret string) {
	channelSecret = secret
}

func (c *Client) setHeader(req *http.Request) *http.Request {
	req.Header.Set("Content-Type", "application/json; charset=UTF-8")
	req.Header.Set("X-LINE-ChannelToken", c.channelAccessToken)
	req.Header.Set("Authorization", "Bearer "+c.channelAccessToken)
	req.Header.Set("User-Agent", "dongri/line-bot-sdk-go")
	return req
}

func (c *Client) do(req *http.Request) (*http.Response, []byte, error) {
	req = c.setHeader(req)
	client := &http.Client{
		Timeout: time.Duration(30 * time.Second),
	}
	res, err := client.Do(req)
	if err != nil {
		return res, nil, err
	}
	defer res.Body.Close()

	if res.StatusCode < 200 || res.StatusCode >= 400 {
		body, readErr := ioutil.ReadAll(res.Body)
		if readErr != nil {
			return res, nil, readErr
		}
		var result APISendResult
		if unmarshalErr := json.Unmarshal(body, &result); unmarshalErr != nil {
			return res, nil, unmarshalErr
		}
		fmt.Println(result)
		return res, nil, errors.New("server error status code: " + strconv.Itoa(res.StatusCode))
	}
	body, err := ioutil.ReadAll(res.Body)
	return res, body, err
}

// ReplyMessage ...
func (c *Client) ReplyMessage(replyToken string, messages ...Message) (*APISendResult, error) {
	replyMessage := struct {
		ReplyToken string    `json:"replyToken"`
		Messages   []Message `json:"messages"`
	}{
		ReplyToken: replyToken,
		Messages:   messages,
	}
	b, err := json.Marshal(replyMessage)
	if err != nil {
		return nil, err
	}
	req, err := http.NewRequest("POST", EndPoint+ReplyMessage, bytes.NewBuffer(b))
	if err != nil {
		return nil, err
	}
	_, body, err := c.do(req)
	if err != nil {
		return nil, err
	}
	var result APISendResult
	if err := json.Unmarshal(body, &result); err != nil {
		return nil, err
	}
	return &result, nil
}

// GetMessageContent ...
func (c *Client) GetMessageContent(messageID string) (*MessageContentResponse, error) {
	endpoint := fmt.Sprintf(EndPoint+GetMessageContent, messageID)
	req, err := http.NewRequest("GET", endpoint, nil)
	if err != nil {
		return nil, err
	}
	res, _, err := c.do(req)
	if err != nil {
		return nil, err
	}
	result := MessageContentResponse{
		Content:       res.Body,
		ContentType:   res.Header.Get("Content-Type"),
		ContentLength: res.ContentLength,
	}
	return &result, nil
}

// GetProfile ...
func (c *Client) GetProfile(userID string) (*UserProfileResponse, error) {
	endpoint := fmt.Sprintf(EndPoint+GetProfile, userID)
	req, err := http.NewRequest("GET", endpoint, nil)
	if err != nil {
		return nil, err
	}
	_, body, err := c.do(req)
	if err != nil {
		return nil, err
	}
	result := UserProfileResponse{}
	if err := json.Unmarshal(body, &result); err != nil {
		return nil, err
	}
	return &result, nil
}

// LeaveGroup ...
func (c *Client) LeaveGroup(groupID string) (*BasicResponse, error) {
	endpoint := fmt.Sprintf(EndPoint+LeaveGroup, groupID)
	req, err := http.NewRequest("POST", endpoint, nil)
	if err != nil {
		return nil, err
	}
	_, body, err := c.do(req)
	if err != nil {
		return nil, err
	}
	result := BasicResponse{}
	if err := json.Unmarshal(body, &result); err != nil {
		return nil, err
	}
	return &result, nil
}

// LeaveRoom ...
func (c *Client) LeaveRoom(roomID string) (*BasicResponse, error) {
	endpoint := fmt.Sprintf(EndPoint+LeaveRoom, roomID)
	req, err := http.NewRequest("POST", endpoint, nil)
	if err != nil {
		return nil, err
	}
	_, body, err := c.do(req)
	if err != nil {
		return nil, err
	}
	result := BasicResponse{}
	if err := json.Unmarshal(body, &result); err != nil {
		return nil, err
	}
	return &result, nil
}
<commit_msg>Fix: content type<commit_after>package linebot

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"time"
)

// API URLs
const (
	EndPoint          = "https://api.line.me"
	PushMessage       = "/v2/bot/message/push"
	ReplyMessage      = "/v2/bot/message/reply"
	GetMessageContent = "/v2/bot/message/%s/content"
	LeaveGroup        = "/v2/bot/group/%s/leave"
	LeaveRoom         = "/v2/bot/room/%s/leave"
	GetProfile        = "/v2/bot/profile/%s"
)

// APISendResult ...
type APISendResult struct {
	Message string `json:"message"`
}

// BasicResponse ...
type BasicResponse struct {
}

// MessageContentResponse ...
type MessageContentResponse struct {
	Content       []byte
	ContentLength int64
	ContentType   string
}

// UserProfileResponse ...
type UserProfileResponse struct {
	UserID        string `json:"userId"`
	DisplayName   string `json:"displayName"`
	PictureURL    string `json:"pictureUrl"`
	StatusMessage string `json:"statusMessage"`
}

// Client ...
type Client struct {
	endPoint           string
	channelAccessToken string
}

var eventHandler EventHandler
var channelSecret string

// NewClient ...
func NewClient(channelAccessToken string) *Client {
	return &Client{
		channelAccessToken: channelAccessToken,
		endPoint:           EndPoint,
	}
}

// SetEventHandler ...
func (c *Client) SetEventHandler(event EventHandler) {
	eventHandler = event
}

// SetChannelSecret ...
func (c *Client) SetChannelSecret(secret string) {
	channelSecret = secret
}

func (c *Client) setHeader(req *http.Request) *http.Request {
	req.Header.Set("Content-Type", "application/json; charset=UTF-8")
	req.Header.Set("X-LINE-ChannelToken", c.channelAccessToken)
	req.Header.Set("Authorization", "Bearer "+c.channelAccessToken)
	req.Header.Set("User-Agent", "dongri/line-bot-sdk-go")
	return req
}

func (c *Client) do(req *http.Request) (*http.Response, []byte, error) {
	req = c.setHeader(req)
	client := &http.Client{
		Timeout: time.Duration(30 * time.Second),
	}
	res, err := client.Do(req)
	if err != nil {
		return res, nil, err
	}
	defer res.Body.Close()

	if res.StatusCode < 200 || res.StatusCode >= 400 {
		body, readErr := ioutil.ReadAll(res.Body)
		if readErr != nil {
			return res, nil, readErr
		}
		var result APISendResult
		if unmarshalErr := json.Unmarshal(body, &result); unmarshalErr != nil {
			return res, nil, unmarshalErr
		}
		fmt.Println(result)
		return res, nil, errors.New("server error status code: " + strconv.Itoa(res.StatusCode))
	}
	body, err := ioutil.ReadAll(res.Body)
	return res, body, err
}

// ReplyMessage ...
func (c *Client) ReplyMessage(replyToken string, messages ...Message) (*APISendResult, error) {
	replyMessage := struct {
		ReplyToken string    `json:"replyToken"`
		Messages   []Message `json:"messages"`
	}{
		ReplyToken: replyToken,
		Messages:   messages,
	}
	b, err := json.Marshal(replyMessage)
	if err != nil {
		return nil, err
	}
	req, err := http.NewRequest("POST", EndPoint+ReplyMessage, bytes.NewBuffer(b))
	if err != nil {
		return nil, err
	}
	_, body, err := c.do(req)
	if err != nil {
		return nil, err
	}
	var result APISendResult
	if err := json.Unmarshal(body, &result); err != nil {
		return nil, err
	}
	return &result, nil
}

// GetMessageContent ...
func (c *Client) GetMessageContent(messageID string) (*MessageContentResponse, error) {
	endpoint := fmt.Sprintf(EndPoint+GetMessageContent, messageID)
	req, err := http.NewRequest("GET", endpoint, nil)
	if err != nil {
		return nil, err
	}
	res, body, err := c.do(req)
	if err != nil {
		return nil, err
	}
	result := MessageContentResponse{
		Content:       body,
		ContentType:   res.Header.Get("Content-Type"),
		ContentLength: res.ContentLength,
	}
	return &result, nil
}

// GetProfile ...
func (c *Client) GetProfile(userID string) (*UserProfileResponse, error) {
	endpoint := fmt.Sprintf(EndPoint+GetProfile, userID)
	req, err := http.NewRequest("GET", endpoint, nil)
	if err != nil {
		return nil, err
	}
	_, body, err := c.do(req)
	if err != nil {
		return nil, err
	}
	result := UserProfileResponse{}
	if err := json.Unmarshal(body, &result); err != nil {
		return nil, err
	}
	return &result, nil
}

// LeaveGroup ...
func (c *Client) LeaveGroup(groupID string) (*BasicResponse, error) {
	endpoint := fmt.Sprintf(EndPoint+LeaveGroup, groupID)
	req, err := http.NewRequest("POST", endpoint, nil)
	if err != nil {
		return nil, err
	}
	_, body, err := c.do(req)
	if err != nil {
		return nil, err
	}
	result := BasicResponse{}
	if err := json.Unmarshal(body, &result); err != nil {
		return nil, err
	}
	return &result, nil
}

// LeaveRoom ...
func (c *Client) LeaveRoom(roomID string) (*BasicResponse, error) {
	endpoint := fmt.Sprintf(EndPoint+LeaveRoom, roomID)
	req, err := http.NewRequest("POST", endpoint, nil)
	if err != nil {
		return nil, err
	}
	_, body, err := c.do(req)
	if err != nil {
		return nil, err
	}
	result := BasicResponse{}
	if err := json.Unmarshal(body, &result); err != nil {
		return nil, err
	}
	return &result, nil
}
<|endoftext|>


<commit_before>// (c) 2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package health

import (
	"net/http"
	"time"

	health "github.com/AppsFlyer/go-sundheit"
	"github.com/AppsFlyer/go-sundheit/checks"
	"github.com/gorilla/rpc/v2"

	"github.com/ava-labs/avalanchego/snow/engine/common"
	"github.com/ava-labs/avalanchego/utils/constants"
	"github.com/ava-labs/avalanchego/utils/json"
	"github.com/ava-labs/avalanchego/utils/logging"
)

// defaultCheckOpts is a Check whose properties represent a default Check
var defaultCheckOpts = check{
	executionPeriod: 30 * time.Second,
	initialDelay:    10 * time.Second,
}

// Health observes a set of vital signs and makes them available through an HTTP
// API.
type Health struct {
	log logging.Logger
	// performs the underlying health checks
	health health.Health
}

// NewService creates a new Health service
func NewService(log logging.Logger) *Health {
	return &Health{log, health.New()}
}

// Handler returns an HTTPHandler providing RPC access to the Health service
func (h *Health) Handler() (*common.HTTPHandler, error) {
	newServer := rpc.NewServer()
	codec := json.NewCodec()
	newServer.RegisterCodec(codec, "application/json")
	newServer.RegisterCodec(codec, "application/json;charset=UTF-8")
	if err := newServer.RegisterService(h, "health"); err != nil {
		return nil, err
	}
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method == http.MethodGet { // GET request --> return 200 if getLiveness returns true, else 503
			if _, healthy := h.health.Results(); healthy {
				w.WriteHeader(http.StatusOK)
			} else {
				w.WriteHeader(http.StatusServiceUnavailable)
			}
		} else {
			newServer.ServeHTTP(w, r) // Other request --> use JSON RPC
		}
	})
	return &common.HTTPHandler{LockOptions: common.NoLock, Handler: handler}, nil
}

// RegisterHeartbeat adds a check with default options and a CheckFn that checks
// the given heartbeater for a recent heartbeat
func (h *Health) RegisterHeartbeat(name string, hb Heartbeater, max time.Duration) error {
	return h.RegisterCheck(&check{
		name:            name,
		checkFn:         HeartbeatCheckFn(hb, max),
		initialDelay:    constants.DefaultHealthCheckInitialDelay,
		executionPeriod: constants.DefaultHealthCheckExecutionPeriod,
	})
}

// RegisterMonotonicCheckFunc adds a Check with default options and the given CheckFn
// After it passes once, its logic (checkFunc) is never run again; it just passes
func (h *Health) RegisterMonotonicCheckFunc(name string, checkFn func() (interface{}, error)) error {
	check := monotonicCheck{check: defaultCheckOpts}
	check.name = name
	check.checkFn = checkFn
	return h.RegisterCheck(check)
}

// RegisterCheck adds the given Check
func (h *Health) RegisterCheck(c checks.Check) error {
	return h.health.RegisterCheck(&health.Config{
		InitialDelay:    constants.DefaultHealthCheckInitialDelay,
		ExecutionPeriod: constants.DefaultHealthCheckExecutionPeriod,
		Check:           c,
	})
}

// GetLivenessArgs are the arguments for GetLiveness
type GetLivenessArgs struct{}

// GetLivenessReply is the response for GetLiveness
type GetLivenessReply struct {
	Checks  map[string]health.Result `json:"checks"`
	Healthy bool                     `json:"healthy"`
}

// GetLiveness returns a summation of the health of the node
func (h *Health) GetLiveness(_ *http.Request, _ *GetLivenessArgs, reply *GetLivenessReply) error {
	h.log.Info("Health: GetLiveness called")
	reply.Checks, reply.Healthy = h.health.Results()
	return nil
}
<commit_msg>cleanup<commit_after>// (c) 2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package health

import (
	"net/http"
	"time"

	health "github.com/AppsFlyer/go-sundheit"
	"github.com/AppsFlyer/go-sundheit/checks"
	"github.com/gorilla/rpc/v2"

	"github.com/ava-labs/avalanchego/snow/engine/common"
	"github.com/ava-labs/avalanchego/utils/constants"
	"github.com/ava-labs/avalanchego/utils/json"
	"github.com/ava-labs/avalanchego/utils/logging"
)

// Health observes a set of vital signs and makes them available through an HTTP
// API.
type Health struct {
	log logging.Logger
	// performs the underlying health checks
	health health.Health
}

// NewService creates a new Health service
func NewService(log logging.Logger) *Health {
	return &Health{log, health.New()}
}

// Handler returns an HTTPHandler providing RPC access to the Health service
func (h *Health) Handler() (*common.HTTPHandler, error) {
	newServer := rpc.NewServer()
	codec := json.NewCodec()
	newServer.RegisterCodec(codec, "application/json")
	newServer.RegisterCodec(codec, "application/json;charset=UTF-8")
	if err := newServer.RegisterService(h, "health"); err != nil {
		return nil, err
	}
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method == http.MethodGet { // GET request --> return 200 if getLiveness returns true, else 503
			if _, healthy := h.health.Results(); healthy {
				w.WriteHeader(http.StatusOK)
			} else {
				w.WriteHeader(http.StatusServiceUnavailable)
			}
		} else {
			newServer.ServeHTTP(w, r) // Other request --> use JSON RPC
		}
	})
	return &common.HTTPHandler{LockOptions: common.NoLock, Handler: handler}, nil
}

// RegisterHeartbeat adds a check with default options and a CheckFn that checks
// the given heartbeater for a recent heartbeat
func (h *Health) RegisterHeartbeat(name string, hb Heartbeater, max time.Duration) error {
	return h.RegisterCheck(&check{
		name:            name,
		checkFn:         HeartbeatCheckFn(hb, max),
		initialDelay:    constants.DefaultHealthCheckInitialDelay,
		executionPeriod: constants.DefaultHealthCheckExecutionPeriod,
	})
}

// RegisterMonotonicCheckFunc adds a Check with default options and the given CheckFn
// After it passes once, its logic (checkFunc) is never run again; it just passes
func (h *Health) RegisterMonotonicCheckFunc(name string, checkFn func() (interface{}, error)) error {
	check := monotonicCheck{
		check: check{
			name: name,
			checkFn: checkFn,
			executionPeriod: constants.DefaultHealthCheckExecutionPeriod,
			initialDelay: constants.DefaultHealthCheckInitialDelay,
		}
	}
	return h.RegisterCheck(check)
}

// RegisterCheck adds the given Check
func (h *Health) RegisterCheck(c checks.Check) error {
	return h.health.RegisterCheck(&health.Config{
		InitialDelay:    constants.DefaultHealthCheckInitialDelay,
		ExecutionPeriod: constants.DefaultHealthCheckExecutionPeriod,
		Check:           c,
	})
}

// GetLivenessArgs are the arguments for GetLiveness
type GetLivenessArgs struct{}

// GetLivenessReply is the response for GetLiveness
type GetLivenessReply struct {
	Checks  map[string]health.Result `json:"checks"`
	Healthy bool                     `json:"healthy"`
}

// GetLiveness returns a summation of the health of the node
func (h *Health) GetLiveness(_ *http.Request, _ *GetLivenessArgs, reply *GetLivenessReply) error {
	h.log.Info("Health: GetLiveness called")
	reply.Checks, reply.Healthy = h.health.Results()
	return nil
}
<|endoftext|>


<commit_before>// +build ignore

package main

import (
	"."
	"flag"
	"github.com/bmizerany/pat"
	"github.com/timeredbull/tsuru/api/app"
	"github.com/timeredbull/tsuru/api/auth"
	"github.com/timeredbull/tsuru/config"
	"github.com/timeredbull/tsuru/api/service"
	"github.com/timeredbull/tsuru/db"
	"github.com/timeredbull/tsuru/log"
	stdlog "log"
	"log/syslog"
	"net/http"
)

func main() {
	var err error
	log.Target, err = syslog.NewLogger(syslog.LOG_INFO, stdlog.LstdFlags)
	if err != nil {
		panic(err)
	}
	configFile := flag.String("config", "/etc/tsuru/tsuru.conf", "tsuru config file")
	dry := flag.Bool("dry", false, "dry-run: does not start the server (for testing purpose)")
	flag.Parse()
	err = config.ReadConfigFile(*configFile)
	if err != nil {
		log.Panic(err.Error())
	}
	db.Session, err = db.Open(config.GetString("database:host"), config.GetString("database:name"))
	if err != nil {
		log.Panic(err.Error())
	}
	defer db.Session.Close()
	m := pat.New()

	m.Post("/services", webserver.Handler(service.CreateHandler))
	m.Get("/services", webserver.Handler(service.ServicesHandler))
	m.Get("/services/types", webserver.Handler(service.ServiceTypesHandler))
	m.Get("/services/:name", webserver.Handler(service.DeleteHandler))
	m.Post("/services/bind", webserver.Handler(service.BindHandler))
	m.Post("/services/unbind", webserver.Handler(service.UnbindHandler))

	m.Get("/apps/:name/delete", webserver.Handler(app.AppDelete))
	m.Get("/apps/:name/clone", webserver.Handler(app.CloneRepositoryHandler))
	m.Get("/apps/:name", webserver.Handler(app.AppInfo))
	m.Post("/apps/:name/application", webserver.Handler(app.Upload))
	m.Get("/apps", webserver.Handler(app.AppList))
	m.Post("/apps", webserver.Handler(app.CreateAppHandler))
	m.Put("/apps/:app/:team", webserver.AuthorizationRequiredHandler(app.GrantAccessToTeamHandler))
	m.Del("/apps/:app/:team", webserver.AuthorizationRequiredHandler(app.RevokeAccessFromTeamHandler))

	m.Post("/users", webserver.Handler(auth.CreateUser))
	m.Post("/users/:email/tokens", webserver.Handler(auth.Login))
	m.Get("/users/check-authorization", webserver.Handler(auth.CheckAuthorization))

	m.Post("/teams", webserver.AuthorizationRequiredHandler(auth.CreateTeam))
	m.Put("/teams/:team/:user", webserver.AuthorizationRequiredHandler(auth.AddUserToTeam))
	m.Del("/teams/:team/:user", webserver.AuthorizationRequiredHandler(auth.RemoveUserFromTeam))

	listen := config.GetString("listen")
	if !*dry {
		log.Fatal(http.ListenAndServe(listen, m))
	}
}
<commit_msg>api/webserver: using the new GetString signature<commit_after>// +build ignore

package main

import (
	"."
	"flag"
	"github.com/bmizerany/pat"
	"github.com/timeredbull/tsuru/api/app"
	"github.com/timeredbull/tsuru/api/auth"
	"github.com/timeredbull/tsuru/config"
	"github.com/timeredbull/tsuru/api/service"
	"github.com/timeredbull/tsuru/db"
	"github.com/timeredbull/tsuru/log"
	stdlog "log"
	"log/syslog"
	"net/http"
)

func main() {
	var err error
	log.Target, err = syslog.NewLogger(syslog.LOG_INFO, stdlog.LstdFlags)
	if err != nil {
		panic(err)
	}
	configFile := flag.String("config", "/etc/tsuru/tsuru.conf", "tsuru config file")
	dry := flag.Bool("dry", false, "dry-run: does not start the server (for testing purpose)")
	flag.Parse()
	err = config.ReadConfigFile(*configFile)
	if err != nil {
		log.Panic(err.Error())
	}
	connString, err := config.GetString("database:host")
	if err != nil {
		panic(err)
	}
	dbName, err := config.GetString("database:name")
	if err != nil {
		panic(err)
	}
	db.Session, err = db.Open(connString, dbName)
	if err != nil {
		log.Panic(err.Error())
	}
	defer db.Session.Close()
	m := pat.New()

	m.Post("/services", webserver.Handler(service.CreateHandler))
	m.Get("/services", webserver.Handler(service.ServicesHandler))
	m.Get("/services/types", webserver.Handler(service.ServiceTypesHandler))
	m.Get("/services/:name", webserver.Handler(service.DeleteHandler))
	m.Post("/services/bind", webserver.Handler(service.BindHandler))
	m.Post("/services/unbind", webserver.Handler(service.UnbindHandler))

	m.Get("/apps/:name/delete", webserver.Handler(app.AppDelete))
	m.Get("/apps/:name/clone", webserver.Handler(app.CloneRepositoryHandler))
	m.Get("/apps/:name", webserver.Handler(app.AppInfo))
	m.Post("/apps/:name/application", webserver.Handler(app.Upload))
	m.Get("/apps", webserver.Handler(app.AppList))
	m.Post("/apps", webserver.Handler(app.CreateAppHandler))
	m.Put("/apps/:app/:team", webserver.AuthorizationRequiredHandler(app.GrantAccessToTeamHandler))
	m.Del("/apps/:app/:team", webserver.AuthorizationRequiredHandler(app.RevokeAccessFromTeamHandler))

	m.Post("/users", webserver.Handler(auth.CreateUser))
	m.Post("/users/:email/tokens", webserver.Handler(auth.Login))
	m.Get("/users/check-authorization", webserver.Handler(auth.CheckAuthorization))

	m.Post("/teams", webserver.AuthorizationRequiredHandler(auth.CreateTeam))
	m.Put("/teams/:team/:user", webserver.AuthorizationRequiredHandler(auth.AddUserToTeam))
	m.Del("/teams/:team/:user", webserver.AuthorizationRequiredHandler(auth.RemoveUserFromTeam))

	listen, err := config.GetString("listen")
	if err != nil {
		panic(err)
	}
	if !*dry {
		log.Fatal(http.ListenAndServe(listen, m))
	}
}
<|endoftext|>


<commit_before>package controllers

import (
	"github.com/lfq7413/tomato/rest"
	"github.com/lfq7413/tomato/types"
	"github.com/lfq7413/tomato/utils"
)

// LogoutController ...
type LogoutController struct {
	ObjectsController
}

// HandleLogOut ...
// @router / [post]
func (l *LogoutController) HandleLogOut() {
	if l.Info != nil && l.Info.SessionToken != "" {
		where := types.M{
			"sessionToken": l.Info.SessionToken,
		}
		// TODO 处理错误
		records, _ := rest.Find(rest.Master(), "_Session", where, types.M{})
		if utils.HasResults(records) {
			results := utils.SliceInterface(records["results"])
			obj := utils.MapInterface(results[0])
			rest.Delete(rest.Master(), "_Session", utils.String(obj["objectId"]))
		}
	}
	l.Data["json"] = types.M{}
	l.ServeJSON()
}

// Get ...
// @router / [get]
func (l *LogoutController) Get() {
	l.ObjectsController.Get()
}

// Delete ...
// @router / [delete]
func (l *LogoutController) Delete() {
	l.ObjectsController.Delete()
}

// Put ...
// @router / [put]
func (l *LogoutController) Put() {
	l.ObjectsController.Put()
}
<commit_msg>重构 logout.go<commit_after>package controllers

import (
	"github.com/lfq7413/tomato/errs"
	"github.com/lfq7413/tomato/rest"
	"github.com/lfq7413/tomato/types"
	"github.com/lfq7413/tomato/utils"
)

// LogoutController 处理 /logout 接口的请求
type LogoutController struct {
	ObjectsController
}

// HandleLogOut 处理用户退出请求
// @router / [post]
func (l *LogoutController) HandleLogOut() {
	if l.Info != nil && l.Info.SessionToken != "" {
		where := types.M{
			"sessionToken": l.Info.SessionToken,
		}
		records, err := rest.Find(rest.Master(), "_Session", where, types.M{})

		if err != nil {
			l.Data["json"] = errs.ErrorToMap(err)
			l.ServeJSON()
			return
		}
		if utils.HasResults(records) {
			results := utils.SliceInterface(records["results"])
			obj := utils.MapInterface(results[0])
			err := rest.Delete(rest.Master(), "_Session", utils.String(obj["objectId"]))
			if err != nil {
				l.Data["json"] = errs.ErrorToMap(err)
				l.ServeJSON()
				return
			}
		}
	}
	l.Data["json"] = types.M{}
	l.ServeJSON()
}

// Get ...
// @router / [get]
func (l *LogoutController) Get() {
	l.ObjectsController.Get()
}

// Delete ...
// @router / [delete]
func (l *LogoutController) Delete() {
	l.ObjectsController.Delete()
}

// Put ...
// @router / [put]
func (l *LogoutController) Put() {
	l.ObjectsController.Put()
}
<|endoftext|>


<commit_before>package physics

import (
	"bytes"
	"compress/flate"

	"github.com/egonelbre/exp/bit"
	"github.com/egonelbre/exp/bit/expgolomb"
)

func encode32(v int32) uint64 { return uint64(bit.AbsEncode(int64(v))) }
func decode32(v uint64) int32 { return int32(bit.AbsDecode(v)) }

func write(w *bit.Writer, v int32, bits uint64) {
	w.WriteBits(uint64(v), bits)
}
func read(r *bit.Reader, bits uint64) int32 {
	v, _ := r.ReadBits(bits)
	return int32(v)
}

func write32(w *bit.Writer, v int32, bits uint64) {
	w.WriteBits(bit.AbsEncode(int64(v)), bits)
}

func read32(r *bit.Reader, bits uint64) int32 {
	v, _ := r.ReadBits(bits)
	return int32(bit.AbsDecode(v))
}

type bits struct {
	ABC uint64
	XYZ uint64
}

func (b *bits) WriteTo(w *bit.Writer) {
	expgolomb.WriteInt(w, int(b.ABC)-0xA)
	expgolomb.WriteInt(w, int(b.XYZ)-0xA)
}

func (b *bits) ReadFrom(r *bit.Reader) {
	abc, _ := expgolomb.ReadInt(r)
	xyz, _ := expgolomb.ReadInt(r)

	b.ABC = uint64(abc + 0xA)
	b.XYZ = uint64(xyz + 0xA)
}

func (b *bits) Update(baseline, current *Frame) {
	for i, cube := range current.Cubes {
		base := baseline.Cubes[i]
		b.ABC = maxbits(b.ABC, cube.A^base.A)
		b.ABC = maxbits(b.ABC, cube.B^base.B)
		b.ABC = maxbits(b.ABC, cube.C^base.C)
		b.XYZ = maxbits(b.XYZ, cube.X^base.X)
		b.XYZ = maxbits(b.XYZ, cube.Y^base.Y)
		b.XYZ = maxbits(b.XYZ, cube.Z^base.Z)
	}
}

func maxbits(a uint64, b int32) uint64 {
	x := bit.AbsEncode(int64(b))
	w := bit.ScanRight(x) + 1
	if a < w {
		return w
	}
	return a
}

func (s *State) Encode() []byte {
	var buf bytes.Buffer

	pack, _ := flate.NewWriter(&buf, flate.DefaultCompression)
	w := bit.NewWriter(pack)

	baseline := s.Baseline()
	current := s.Current()

	var bits bits
	bits.Update(baseline, current)
	bits.WriteTo(w)

	for i, cube := range current.Cubes {
		base := baseline.Cubes[i]

		write(w, cube.Interacting^base.Interacting, 1)
		write(w, cube.Largest^base.Largest, 2)

		write32(w, cube.A^base.A, bits.ABC)
		write32(w, cube.B^base.B, bits.ABC)
		write32(w, cube.C^base.C, bits.ABC)

		write32(w, cube.X^base.X, bits.XYZ)
		write32(w, cube.Y^base.Y, bits.XYZ)
		write32(w, cube.Z^base.Z, bits.XYZ)
	}

	w.Close()
	pack.Close()
	return buf.Bytes()
}

func (s *State) Decode(snapshot []byte) {
	buf := bytes.NewBuffer(snapshot)
	pack := flate.NewReader(buf)
	r := bit.NewReader(pack)

	baseline := s.Baseline()
	current := s.Current()

	var bits bits
	bits.ReadFrom(r)

	for i := range current.Cubes {
		base := baseline.Cubes[i]
		cube := &current.Cubes[i]

		cube.Interacting = read(r, 1) ^ base.Interacting
		cube.Largest = read(r, 2) ^ base.Largest

		cube.A = read32(r, bits.ABC) ^ base.A
		cube.B = read32(r, bits.ABC) ^ base.B
		cube.C = read32(r, bits.ABC) ^ base.C

		cube.X = read32(r, bits.XYZ) ^ base.X
		cube.Y = read32(r, bits.XYZ) ^ base.Y
		cube.Z = read32(r, bits.XYZ) ^ base.Z
	}
}
<commit_msg>Fix things<commit_after>package physics

import (
	"bytes"
	"compress/flate"

	"github.com/egonelbre/exp/bit"
)

func encode32(v int32) uint64 { return uint64(bit.AbsEncode(int64(v))) }
func decode32(v uint64) int32 { return int32(bit.AbsDecode(v)) }

func write(w *bit.Writer, v int32, bits uint64) {
	w.WriteBits(uint64(v), bits)
}
func read(r *bit.Reader, bits uint64) int32 {
	v, _ := r.ReadBits(bits)
	return int32(v)
}

func write32(w *bit.Writer, v int32, bits uint64) {
	w.WriteBits(bit.AbsEncode(int64(v)), bits)
}

func read32(r *bit.Reader, bits uint64) int32 {
	v, _ := r.ReadBits(bits)
	return int32(bit.AbsDecode(v))
}

func maxbits(vs ...int32) (r uint64) {
	r = 0
	for _, x := range vs {
		if x != 0 {
			bits := bit.ScanRight(bit.AbsEncode(int64(x))) + 4
			if r < bits {
				r = bits
			}
		}
	}
	return
}

func deltabits(base, cube *Cube) uint64 {
	return maxbits(
		cube.Interacting^base.Interacting,
		cube.Largest^base.Largest,
		cube.A^base.A,
		cube.B^base.B,
		cube.C^base.C,
		cube.X^base.X,
		cube.Y^base.Y,
		cube.Z^base.Z,
	)
}

func (s *State) Encode() []byte {
	var buf bytes.Buffer

	pack, _ := flate.NewWriter(&buf, flate.DefaultCompression)
	w := bit.NewWriter(pack)

	baseline := s.Baseline()
	current := s.Current()

	for i, cube := range current.Cubes {
		base := baseline.Cubes[i]

		//bits := deltabits(&base, &cube)
		bits := uint64(32)

		//w.WriteBits(bits, 6)
		//if bits == 0 {
		//	continue
		//}

		//if bits == 0 {
		//	w.WriteBit(0)
		//	continue
		//}
		//w.WriteBit(1)
		//w.WriteBits(bits, 6)
		//expgolomb.WriteInt(w, int(bits)-9)

		write(w, cube.Interacting^base.Interacting, 1)
		write(w, cube.Largest^base.Largest, 2)

		write32(w, cube.A^base.A, bits)
		write32(w, cube.B^base.B, bits)
		write32(w, cube.C^base.C, bits)
		write32(w, cube.X^base.X, bits)
		write32(w, cube.Y^base.Y, bits)
		write32(w, cube.Z^base.Z, bits)
	}

	w.Close()
	pack.Close()
	return buf.Bytes()
}

func (s *State) Decode(snapshot []byte) {
	buf := bytes.NewBuffer(snapshot)
	pack := flate.NewReader(buf)
	r := bit.NewReader(pack)

	baseline := s.Baseline()
	current := s.Current()

	for i := range current.Cubes {
		base := baseline.Cubes[i]
		cube := &current.Cubes[i]

		bits := uint64(32)
		//bits, _ := r.ReadBits(6)
		//if bits == 0 {
		//	continue
		//}

		//xbits, _ := expgolomb.ReadInt(r)
		//if xbits == 0 {
		//	continue
		//}
		//bits := uint64(xbits + 9)

		cube.Interacting = read(r, 1) ^ base.Interacting
		cube.Largest = read(r, 2) ^ base.Largest

		cube.A = read32(r, bits) ^ base.A
		cube.B = read32(r, bits) ^ base.B
		cube.C = read32(r, bits) ^ base.C

		cube.X = read32(r, bits) ^ base.X
		cube.Y = read32(r, bits) ^ base.Y
		cube.Z = read32(r, bits) ^ base.Z
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 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 clientbuilder

import (
	"k8s.io/client-go/discovery"
	clientset "k8s.io/client-go/kubernetes"
	restclient "k8s.io/client-go/rest"
	"k8s.io/klog/v2"
)

// ControllerClientBuilder allows you to get clients and configs for controllers
// Please note a copy also exists in staging/src/k8s.io/cloud-provider/cloud.go
// TODO: Extract this into a separate controller utilities repo (issues/68947)
type ControllerClientBuilder interface {
	Config(name string) (*restclient.Config, error)
	ConfigOrDie(name string) *restclient.Config
	Client(name string) (clientset.Interface, error)
	ClientOrDie(name string) clientset.Interface
	DiscoveryClient(name string) (discovery.DiscoveryInterface, error)
	DiscoveryClientOrDie(name string) discovery.DiscoveryInterface
}

// SimpleControllerClientBuilder returns a fixed client with different user agents
type SimpleControllerClientBuilder struct {
	// ClientConfig is a skeleton config to clone and use as the basis for each controller client
	ClientConfig *restclient.Config
}

// Config returns a client config for a fixed client
func (b SimpleControllerClientBuilder) Config(name string) (*restclient.Config, error) {
	clientConfig := *b.ClientConfig
	return restclient.AddUserAgent(&clientConfig, name), nil
}

// ConfigOrDie returns a client config if no error from previous config func.
// If it gets an error getting the client, it will log the error and kill the process it's running in.
func (b SimpleControllerClientBuilder) ConfigOrDie(name string) *restclient.Config {
	clientConfig, err := b.Config(name)
	if err != nil {
		klog.Fatal(err)
	}
	return clientConfig
}

// Client returns a clientset.Interface built from the ClientBuilder
func (b SimpleControllerClientBuilder) Client(name string) (clientset.Interface, error) {
	clientConfig, err := b.Config(name)
	if err != nil {
		return nil, err
	}
	return clientset.NewForConfig(clientConfig)
}

// ClientOrDie returns a clientset.interface built from the ClientBuilder with no error.
// If it gets an error getting the client, it will log the error and kill the process it's running in.
func (b SimpleControllerClientBuilder) ClientOrDie(name string) clientset.Interface {
	client, err := b.Client(name)
	if err != nil {
		klog.Fatal(err)
	}
	return client
}

// DiscoveryClientOrDie returns a discovery.DiscoveryInterface built from the ClientBuilder
// Discovery is special because it will artificially pump the burst quite high to handle the many discovery requests.
func (b SimpleControllerClientBuilder) DiscoveryClient(name string) (discovery.DiscoveryInterface, error) {
	clientConfig, err := b.Config(name)
	if err != nil {
		return nil, err
	}
	// Discovery makes a lot of requests infrequently.  This allows the burst to succeed and refill to happen
	// in just a few seconds.
	clientConfig.Burst = 200
	clientConfig.QPS = 20
	return clientset.NewForConfig(clientConfig)
}

// DiscoveryClientOrDie returns a discovery.DiscoveryInterface built from the ClientBuilder with no error.
// Discovery is special because it will artificially pump the burst quite high to handle the many discovery requests.
// If it gets an error getting the client, it will log the error and kill the process it's running in.
func (b SimpleControllerClientBuilder) DiscoveryClientOrDie(name string) discovery.DiscoveryInterface {
	client, err := b.DiscoveryClient(name)
	if err != nil {
		klog.Fatal(err)
	}
	return client
}
<commit_msg>Fix a typo in comment<commit_after>/*
Copyright 2016 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 clientbuilder

import (
	"k8s.io/client-go/discovery"
	clientset "k8s.io/client-go/kubernetes"
	restclient "k8s.io/client-go/rest"
	"k8s.io/klog/v2"
)

// ControllerClientBuilder allows you to get clients and configs for controllers
// Please note a copy also exists in staging/src/k8s.io/cloud-provider/cloud.go
// TODO: Extract this into a separate controller utilities repo (issues/68947)
type ControllerClientBuilder interface {
	Config(name string) (*restclient.Config, error)
	ConfigOrDie(name string) *restclient.Config
	Client(name string) (clientset.Interface, error)
	ClientOrDie(name string) clientset.Interface
	DiscoveryClient(name string) (discovery.DiscoveryInterface, error)
	DiscoveryClientOrDie(name string) discovery.DiscoveryInterface
}

// SimpleControllerClientBuilder returns a fixed client with different user agents
type SimpleControllerClientBuilder struct {
	// ClientConfig is a skeleton config to clone and use as the basis for each controller client
	ClientConfig *restclient.Config
}

// Config returns a client config for a fixed client
func (b SimpleControllerClientBuilder) Config(name string) (*restclient.Config, error) {
	clientConfig := *b.ClientConfig
	return restclient.AddUserAgent(&clientConfig, name), nil
}

// ConfigOrDie returns a client config if no error from previous config func.
// If it gets an error getting the client, it will log the error and kill the process it's running in.
func (b SimpleControllerClientBuilder) ConfigOrDie(name string) *restclient.Config {
	clientConfig, err := b.Config(name)
	if err != nil {
		klog.Fatal(err)
	}
	return clientConfig
}

// Client returns a clientset.Interface built from the ClientBuilder
func (b SimpleControllerClientBuilder) Client(name string) (clientset.Interface, error) {
	clientConfig, err := b.Config(name)
	if err != nil {
		return nil, err
	}
	return clientset.NewForConfig(clientConfig)
}

// ClientOrDie returns a clientset.interface built from the ClientBuilder with no error.
// If it gets an error getting the client, it will log the error and kill the process it's running in.
func (b SimpleControllerClientBuilder) ClientOrDie(name string) clientset.Interface {
	client, err := b.Client(name)
	if err != nil {
		klog.Fatal(err)
	}
	return client
}

// DiscoveryClient returns a discovery.DiscoveryInterface built from the ClientBuilder
// Discovery is special because it will artificially pump the burst quite high to handle the many discovery requests.
func (b SimpleControllerClientBuilder) DiscoveryClient(name string) (discovery.DiscoveryInterface, error) {
	clientConfig, err := b.Config(name)
	if err != nil {
		return nil, err
	}
	// Discovery makes a lot of requests infrequently.  This allows the burst to succeed and refill to happen
	// in just a few seconds.
	clientConfig.Burst = 200
	clientConfig.QPS = 20
	return clientset.NewForConfig(clientConfig)
}

// DiscoveryClientOrDie returns a discovery.DiscoveryInterface built from the ClientBuilder with no error.
// Discovery is special because it will artificially pump the burst quite high to handle the many discovery requests.
// If it gets an error getting the client, it will log the error and kill the process it's running in.
func (b SimpleControllerClientBuilder) DiscoveryClientOrDie(name string) discovery.DiscoveryInterface {
	client, err := b.DiscoveryClient(name)
	if err != nil {
		klog.Fatal(err)
	}
	return client
}
<|endoftext|>


<commit_before>/*
Copyright 2019 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.
*/

// This file contains functionality and constants for the L7-ILB feature
// Since this also currently affects backend resources (since they are alpha-regional
// instead of ga-global), this feature is also included in pkg/backends/features.go
package features

import (
	"context"
	"errors"
	"fmt"

	"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/filter"
	"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/meta"
	"k8s.io/klog"
	"k8s.io/legacy-cloud-providers/gce"
)

var ErrSubnetNotFound = errors.New("active subnet not found")

// Get Subnet source range for ILB
// TODO: (shance) refactor to use filter
func ILBSubnetSourceRange(cloud *gce.Cloud, region string) (string, error) {
	subnets, err := cloud.Compute().AlphaSubnetworks().List(context.Background(), region, filter.None)
	if err != nil {
		return "", fmt.Errorf("error obtaining subnets for region %s, %v", region, err)
	}

	for _, subnet := range subnets {
		if subnet.Role == "ACTIVE" && subnet.Purpose == "INTERNAL_HTTPS_LOAD_BALANCER" {
			klog.V(3).Infof("Found L7-ILB Subnet %s - %s", subnet.Name, subnet.IpCidrRange)
			return subnet.IpCidrRange, nil
		}
	}
	return "", ErrSubnetNotFound
}

// L7ILBVersion is a helper to get the version of L7-ILB
func L7ILBVersions() *ResourceVersions {
	return versionsFromFeatures([]string{FeatureL7ILB})
}

// L7ILBScope is a helper to get the scope of L7-ILB
func L7ILBScope() meta.KeyType {
	return scopeFromFeatures([]string{FeatureL7ILB})
}
<commit_msg>Update list subnets call to Beta for L7-ILB<commit_after>/*
Copyright 2019 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.
*/

// This file contains functionality and constants for the L7-ILB feature
// Since this also currently affects backend resources (since they are alpha-regional
// instead of ga-global), this feature is also included in pkg/backends/features.go
package features

import (
	"context"
	"errors"
	"fmt"

	"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/filter"
	"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/meta"
	"k8s.io/klog"
	"k8s.io/legacy-cloud-providers/gce"
)

var ErrSubnetNotFound = errors.New("active subnet not found")

// Get Subnet source range for ILB
// TODO: (shance) refactor to use filter
func ILBSubnetSourceRange(cloud *gce.Cloud, region string) (string, error) {
	subnets, err := cloud.Compute().BetaSubnetworks().List(context.Background(), region, filter.None)
	if err != nil {
		return "", fmt.Errorf("error obtaining subnets for region %s, %v", region, err)
	}

	for _, subnet := range subnets {
		if subnet.Role == "ACTIVE" && subnet.Purpose == "INTERNAL_HTTPS_LOAD_BALANCER" {
			klog.V(3).Infof("Found L7-ILB Subnet %s - %s", subnet.Name, subnet.IpCidrRange)
			return subnet.IpCidrRange, nil
		}
	}
	return "", ErrSubnetNotFound
}

// L7ILBVersion is a helper to get the version of L7-ILB
func L7ILBVersions() *ResourceVersions {
	return versionsFromFeatures([]string{FeatureL7ILB})
}

// L7ILBScope is a helper to get the scope of L7-ILB
func L7ILBScope() meta.KeyType {
	return scopeFromFeatures([]string{FeatureL7ILB})
}
<|endoftext|>


<commit_before>// +build csharp

package modules

// To enable the message height limit module, you need .NET Core on your server

// #cgo LDFLAGS: -L../../3rdParty/MessageHeightTwitch/c-interop -lcoreruncommon -ldl -lstdc++
// #include "../../3rdParty/MessageHeightTwitch/c-interop/exports.h"
// #include <stdlib.h>
import "C"

import (
	"errors"
	"fmt"
	"math"
	"os"
	"path/filepath"
	"strings"
	"unsafe"

	"github.com/pajlada/pajbot2/pkg"
	"github.com/pajlada/pajbot2/pkg/utils"
)

var _ pkg.Module = &MessageHeightLimit{}

func floatPtr(v float32) *float32 {
	return &v
}

var messageHeightLimitSpec *moduleSpec

func init() {
	messageHeightLimitSpec = &moduleSpec{
		id:    "message_height_limit",
		name:  "Message height limit",
		maker: NewMessageHeightLimit,

		enabledByDefault: false,

		parameters: map[string]*moduleParameterSpec{
			"HeightLimit": &moduleParameterSpec{
				description:  "Max height of a message before it's timed out",
				defaultValue: floatPtr(95),
			},
		},
	}

	Register(messageHeightLimitSpec)
}

type MessageHeightLimit struct {
	botChannel pkg.BotChannel

	server *server

	HeightLimit floatParameter `json:",omitempty"`

	userViolationCount map[string]int
}

func NewMessageHeightLimit() pkg.Module {
	return &MessageHeightLimit{
		server: &_server,

		HeightLimit: floatParameter{
			defaultValue: messageHeightLimitSpec.parameters["HeightLimit"].defaultValue.(*float32),
		},
		userViolationCount: make(map[string]int),
	}
}

var clrInitialized = false

var messageHeightLimitLibraryInitialized = false
var charMapPath string

func initCLR() error {
	executableDir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		return err
	}

	fmt.Println("Executable dir", executableDir)
	fmt.Println("os args 0:", os.Args[0])

	clrPath := utils.GetEnv("LIBCOREFOLDER", "/usr/share/dotnet/shared/Microsoft.NETCore.App/2.1.5")

	// Path to our own executable
	clr1 := C.CString(executableDir + "/bot")

	fmt.Println(executableDir)

	// Folder where libcoreclr.so is located
	clr2 := C.CString(clrPath)

	// Path to library we want to use
	clr3 := C.CString(executableDir + "/MessageHeightTwitch.dll")

	var res C.int

	res = C.LoadCLRRuntime(
		clr1,
		clr2,
		clr3)

	C.free(unsafe.Pointer(clr1))
	C.free(unsafe.Pointer(clr2))
	C.free(unsafe.Pointer(clr3))

	if res != 0 {
		return errors.New("Failed to load CLR Runtime")
	}

	charMapPath = executableDir + "/charmap.bin.gz"

	clrInitialized = true

	return nil
}

func initChannel(channelName string) error {
	channel := C.CString(channelName)

	res := C.InitChannel(channel)

	if res != 1 {
		return errors.New("Failed to init Channel " + channelName)
	}

	C.free(unsafe.Pointer(channel))

	return nil
}

func initMessageHeightLimitLibrary() error {
	charMap := C.CString(charMapPath)

	fmt.Println(charMapPath)

	res := C.InitCharMap(charMap)

	C.free(unsafe.Pointer(charMap))

	if res != 1 {
		return errors.New(fmt.Sprintf("Failed to init CharMap: %d", int(res)))
	}

	messageHeightLimitLibraryInitialized = true

	return nil
}

func (m *MessageHeightLimit) Initialize(botChannel pkg.BotChannel, settings []byte) (err error) {
	m.botChannel = botChannel

	if !clrInitialized {
		err = initCLR()
		if err != nil {
			return
		}

		err = initMessageHeightLimitLibrary()
	}

	if err := initChannel(botChannel.ChannelName()); err != nil {
		return err
	}

	if err := loadModule(settings, m); err != nil {
		fmt.Println("Error loading module:", err)
	}

	return
}

func (m *MessageHeightLimit) Disable() error {
	return nil
}

func (m *MessageHeightLimit) Spec() pkg.ModuleSpec {
	return messageHeightLimitSpec
}

func (m *MessageHeightLimit) BotChannel() pkg.BotChannel {
	return m.botChannel
}

func (m *MessageHeightLimit) OnWhisper(bot pkg.Sender, user pkg.User, message pkg.Message) error {
	return nil
}

func (m *MessageHeightLimit) getHeight(channel pkg.Channel, user pkg.User, message pkg.Message) float32 {
	channelString := C.CString(channel.GetChannel())
	input := C.CString(message.GetText())
	loginName := C.CString(user.GetName())
	displayName := C.CString(user.GetDisplayName())

	var emoteStrings []*C.char

	var te []C.TwitchEmote

	reader := message.GetTwitchReader()
	for reader.Next() {
		emote := reader.Get()
		emoteCode := C.CString(emote.GetName())
		emoteURL := C.CString(fmt.Sprintf("https://static-cdn.jtvnw.net/emoticons/v1/%s/1.0", emote.GetID()))

		te = append(te, C.TwitchEmote{emoteCode, emoteURL})

		emoteStrings = append(emoteStrings, emoteCode)
		emoteStrings = append(emoteStrings, emoteURL)
	}

	var pArray unsafe.Pointer

	if len(te) > 0 {
		pArray = unsafe.Pointer(&te[0])
	}

	badgeCount := C.int(len(user.GetBadges()))

	height := C.CalculateMessageHeightDirect(
		channelString,
		input,                      // Message text
		loginName,                  // Login name
		displayName,                // Display name
		badgeCount,                 // Badge count
		((*C.TwitchEmote)(pArray)), // Array of emotes
		C.int(len(te)),             // Emote array size
	)

	C.free(unsafe.Pointer(channelString))
	C.free(unsafe.Pointer(input))
	C.free(unsafe.Pointer(loginName))
	C.free(unsafe.Pointer(displayName))

	for _, str := range emoteStrings {
		C.free(unsafe.Pointer(str))
	}

	return float32(height)
}

func (m *MessageHeightLimit) OnMessage(bot pkg.Sender, channel pkg.Channel, user pkg.User, message pkg.Message, action pkg.Action) error {
	if !messageHeightLimitLibraryInitialized {
		return nil
	}

	if user.GetName() == "gazatu2" {
		return nil
	}

	if user.GetName() == "supibot" {
		return nil
	}

	if user.GetName() == "titlechange_bot" {
		return nil
	}

	if user.IsModerator() || user.IsBroadcaster(channel) {
		if strings.HasPrefix(message.GetText(), "!") {
			parts := strings.Split(message.GetText(), " ")
			if parts[0] == "!heightlimit" {
				if len(parts) >= 2 {
					if err := m.HeightLimit.Parse(parts[1]); err != nil {
						bot.Mention(channel, user, err.Error())
						return nil
					}

					bot.Mention(channel, user, "Height limit set to "+utils.Float32ToString(m.HeightLimit.Get()))
					saveModule(m)
				} else {
					bot.Mention(channel, user, "Height limit is "+utils.Float32ToString(m.HeightLimit.Get()))
				}

				return nil
			}

			if parts[0] == "!heighttest" {
				height := m.getHeight(channel, user, message)
				bot.Mention(channel, user, fmt.Sprintf("your message height is %.2f", height))
				return nil
			}
		}
	}

	const maxTimeoutLength = 1800

	height := m.getHeight(channel, user, message)
	// bot.Mention(channel, user, fmt.Sprintf("Message height: %f\n", height))

	if height > m.HeightLimit.Get() {
		// Message height is too tall
		messageLength := len([]rune(message.GetText()))
		var fitsIn7Bit int
		var doesntFitIn7Bit int
		for _, r := range message.GetText() {
			if r > 0x7a || r < 0x20 {
				doesntFitIn7Bit++
			} else {
				fitsIn7Bit++
			}
		}

		fmt.Printf("Message length: %d. Fits: %d. Don't fit: %d\n", messageLength, doesntFitIn7Bit, fitsIn7Bit)
		var ratio float32
		ratio = float32(doesntFitIn7Bit) / float32(messageLength)
		var reason string
		userViolations := 0
		timeoutDuration := int(math.Min(math.Pow(float64(height-m.HeightLimit.Get()), 1.2), maxTimeoutLength))
		if ratio > 0.5 {
			timeoutDuration = timeoutDuration + 90
		}

		if ratio > 0.5 && height > 140.0 {
			m.userViolationCount[user.GetID()] = m.userViolationCount[user.GetID()] + 1
			userViolations = m.userViolationCount[user.GetID()]
			timeoutDuration = timeoutDuration * userViolations
			timeoutDuration = utils.MinInt(3600*24*7, timeoutDuration)
			reason = fmt.Sprintf("Your message is too tall: %.1f - %.3f A", height, ratio)
			bot.Whisper(user, fmt.Sprintf("Your message is too long and contains too many non-ascii characters. Your next timeout will be multiplied by %d", userViolations))
		} else {
			reason = fmt.Sprintf("Your message is too tall: %.1f - %.3f", height, ratio)
		}

		reason = fmt.Sprintf("Your message is too tall: %.0f (%d)", height, userViolations)
		action.Set(pkg.Timeout{
			Duration: timeoutDuration,
			Reason:   reason,
		})
	}

	return nil
}
<commit_msg>add a minimum timeout duration<commit_after>// +build csharp

package modules

// To enable the message height limit module, you need .NET Core on your server

// #cgo LDFLAGS: -L../../3rdParty/MessageHeightTwitch/c-interop -lcoreruncommon -ldl -lstdc++
// #include "../../3rdParty/MessageHeightTwitch/c-interop/exports.h"
// #include <stdlib.h>
import "C"

import (
	"errors"
	"fmt"
	"math"
	"os"
	"path/filepath"
	"strings"
	"unsafe"

	"github.com/pajlada/pajbot2/pkg"
	"github.com/pajlada/pajbot2/pkg/utils"
)

var _ pkg.Module = &MessageHeightLimit{}

func floatPtr(v float32) *float32 {
	return &v
}

var messageHeightLimitSpec *moduleSpec

func init() {
	messageHeightLimitSpec = &moduleSpec{
		id:    "message_height_limit",
		name:  "Message height limit",
		maker: NewMessageHeightLimit,

		enabledByDefault: false,

		parameters: map[string]*moduleParameterSpec{
			"HeightLimit": &moduleParameterSpec{
				description:  "Max height of a message before it's timed out",
				defaultValue: floatPtr(95),
			},
		},
	}

	Register(messageHeightLimitSpec)
}

type MessageHeightLimit struct {
	botChannel pkg.BotChannel

	server *server

	HeightLimit floatParameter `json:",omitempty"`

	userViolationCount map[string]int
}

func NewMessageHeightLimit() pkg.Module {
	return &MessageHeightLimit{
		server: &_server,

		HeightLimit: floatParameter{
			defaultValue: messageHeightLimitSpec.parameters["HeightLimit"].defaultValue.(*float32),
		},
		userViolationCount: make(map[string]int),
	}
}

var clrInitialized = false

var messageHeightLimitLibraryInitialized = false
var charMapPath string

func initCLR() error {
	executableDir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		return err
	}

	fmt.Println("Executable dir", executableDir)
	fmt.Println("os args 0:", os.Args[0])

	clrPath := utils.GetEnv("LIBCOREFOLDER", "/usr/share/dotnet/shared/Microsoft.NETCore.App/2.1.5")

	// Path to our own executable
	clr1 := C.CString(executableDir + "/bot")

	fmt.Println(executableDir)

	// Folder where libcoreclr.so is located
	clr2 := C.CString(clrPath)

	// Path to library we want to use
	clr3 := C.CString(executableDir + "/MessageHeightTwitch.dll")

	var res C.int

	res = C.LoadCLRRuntime(
		clr1,
		clr2,
		clr3)

	C.free(unsafe.Pointer(clr1))
	C.free(unsafe.Pointer(clr2))
	C.free(unsafe.Pointer(clr3))

	if res != 0 {
		return errors.New("Failed to load CLR Runtime")
	}

	charMapPath = executableDir + "/charmap.bin.gz"

	clrInitialized = true

	return nil
}

func initChannel(channelName string) error {
	channel := C.CString(channelName)

	res := C.InitChannel(channel)

	if res != 1 {
		return errors.New("Failed to init Channel " + channelName)
	}

	C.free(unsafe.Pointer(channel))

	return nil
}

func initMessageHeightLimitLibrary() error {
	charMap := C.CString(charMapPath)

	fmt.Println(charMapPath)

	res := C.InitCharMap(charMap)

	C.free(unsafe.Pointer(charMap))

	if res != 1 {
		return errors.New(fmt.Sprintf("Failed to init CharMap: %d", int(res)))
	}

	messageHeightLimitLibraryInitialized = true

	return nil
}

func (m *MessageHeightLimit) Initialize(botChannel pkg.BotChannel, settings []byte) (err error) {
	m.botChannel = botChannel

	if !clrInitialized {
		err = initCLR()
		if err != nil {
			return
		}

		err = initMessageHeightLimitLibrary()
	}

	if err := initChannel(botChannel.ChannelName()); err != nil {
		return err
	}

	if err := loadModule(settings, m); err != nil {
		fmt.Println("Error loading module:", err)
	}

	return
}

func (m *MessageHeightLimit) Disable() error {
	return nil
}

func (m *MessageHeightLimit) Spec() pkg.ModuleSpec {
	return messageHeightLimitSpec
}

func (m *MessageHeightLimit) BotChannel() pkg.BotChannel {
	return m.botChannel
}

func (m *MessageHeightLimit) OnWhisper(bot pkg.Sender, user pkg.User, message pkg.Message) error {
	return nil
}

func (m *MessageHeightLimit) getHeight(channel pkg.Channel, user pkg.User, message pkg.Message) float32 {
	channelString := C.CString(channel.GetChannel())
	input := C.CString(message.GetText())
	loginName := C.CString(user.GetName())
	displayName := C.CString(user.GetDisplayName())

	var emoteStrings []*C.char

	var te []C.TwitchEmote

	reader := message.GetTwitchReader()
	for reader.Next() {
		emote := reader.Get()
		emoteCode := C.CString(emote.GetName())
		emoteURL := C.CString(fmt.Sprintf("https://static-cdn.jtvnw.net/emoticons/v1/%s/1.0", emote.GetID()))

		te = append(te, C.TwitchEmote{emoteCode, emoteURL})

		emoteStrings = append(emoteStrings, emoteCode)
		emoteStrings = append(emoteStrings, emoteURL)
	}

	var pArray unsafe.Pointer

	if len(te) > 0 {
		pArray = unsafe.Pointer(&te[0])
	}

	badgeCount := C.int(len(user.GetBadges()))

	height := C.CalculateMessageHeightDirect(
		channelString,
		input,                      // Message text
		loginName,                  // Login name
		displayName,                // Display name
		badgeCount,                 // Badge count
		((*C.TwitchEmote)(pArray)), // Array of emotes
		C.int(len(te)),             // Emote array size
	)

	C.free(unsafe.Pointer(channelString))
	C.free(unsafe.Pointer(input))
	C.free(unsafe.Pointer(loginName))
	C.free(unsafe.Pointer(displayName))

	for _, str := range emoteStrings {
		C.free(unsafe.Pointer(str))
	}

	return float32(height)
}

func (m *MessageHeightLimit) OnMessage(bot pkg.Sender, channel pkg.Channel, user pkg.User, message pkg.Message, action pkg.Action) error {
	if !messageHeightLimitLibraryInitialized {
		return nil
	}

	if user.GetName() == "gazatu2" {
		return nil
	}

	if user.GetName() == "supibot" {
		return nil
	}

	if user.GetName() == "titlechange_bot" {
		return nil
	}

	if user.IsModerator() || user.IsBroadcaster(channel) || user.HasPermission(channel, pkg.PermissionModeration) {
		if strings.HasPrefix(message.GetText(), "!") {
			parts := strings.Split(message.GetText(), " ")
			if parts[0] == "!heightlimit" {
				if len(parts) >= 2 {
					if err := m.HeightLimit.Parse(parts[1]); err != nil {
						bot.Mention(channel, user, err.Error())
						return nil
					}

					bot.Mention(channel, user, "Height limit set to "+utils.Float32ToString(m.HeightLimit.Get()))
					saveModule(m)
				} else {
					bot.Mention(channel, user, "Height limit is "+utils.Float32ToString(m.HeightLimit.Get()))
				}

				return nil
			}

			if parts[0] == "!heighttest" {
				height := m.getHeight(channel, user, message)
				bot.Mention(channel, user, fmt.Sprintf("your message height is %.2f", height))
				return nil
			}
		}
	}

	const minTimeoutLength = 10
	const maxTimeoutLength = 1800

	height := m.getHeight(channel, user, message)
	// bot.Mention(channel, user, fmt.Sprintf("Message height: %f\n", height))

	if height > m.HeightLimit.Get() {
		// Message height is too tall
		messageLength := len([]rune(message.GetText()))
		var fitsIn7Bit int
		var doesntFitIn7Bit int
		for _, r := range message.GetText() {
			if r > 0x7a || r < 0x20 {
				doesntFitIn7Bit++
			} else {
				fitsIn7Bit++
			}
		}

		fmt.Printf("Message length: %d. Fits: %d. Don't fit: %d\n", messageLength, doesntFitIn7Bit, fitsIn7Bit)
		var ratio float32
		ratio = float32(doesntFitIn7Bit) / float32(messageLength)
		var reason string
		userViolations := 0
		timeoutDuration := int(math.Min(math.Pow(float64(height-m.HeightLimit.Get()), 1.2), maxTimeoutLength))
		if ratio > 0.5 {
			timeoutDuration = timeoutDuration + 90
		}

		timeoutDuration = utils.MaxInt(minTimeoutLength, timeoutDuration)

		if ratio > 0.5 && height > 140.0 {
			m.userViolationCount[user.GetID()] = m.userViolationCount[user.GetID()] + 1
			userViolations = m.userViolationCount[user.GetID()]
			timeoutDuration = timeoutDuration * userViolations
			timeoutDuration = utils.MinInt(3600*24*7, timeoutDuration)
			reason = fmt.Sprintf("Your message is too tall: %.1f - %.3f A", height, ratio)
			bot.Whisper(user, fmt.Sprintf("Your message is too long and contains too many non-ascii characters. Your next timeout will be multiplied by %d", userViolations))
		} else {
			reason = fmt.Sprintf("Your message is too tall: %.1f - %.3f", height, ratio)
		}

		reason = fmt.Sprintf("Your message is too tall: %.0f (%d)", height, userViolations)
		action.Set(pkg.Timeout{
			Duration: timeoutDuration,
			Reason:   reason,
		})
	}

	return nil
}
<|endoftext|>


<commit_before>package termite

import (
	"github.com/hanwen/go-fuse/fuse"
	"log"
	"io/ioutil"
	"os"
	"path/filepath"
	"syscall"
	"testing"
	"time"
)

var _ = log.Printf

func testStat(t *testing.T, n string) *os.FileInfo {
	t.Logf("stat %q", n)
	f, _ := os.Lstat(n)
	return f
}

func getattr(t *testing.T, n string) *FileAttr {
	t.Logf("getattr %q", n)
	fi, _ := os.Lstat(n)
	a := FileAttr{
		FileInfo: fi,
	}
	if !a.Deletion() {
		a.ReadFromFs(n)
	}
	return &a
}

func TestAttrCacheNil(t *testing.T) {
	ac := NewAttributeCache(
		func(n string) *FileAttr {
		return nil
	},
		func(n string) *os.FileInfo {
		return nil
	})

	r := ac.Get("")
	if r == nil || !r.Deletion() {
		t.Errorf("should return deletion for error, got: %v", r)
	}
}

func TestAttrCache(t *testing.T) {
	dir, err := ioutil.TempDir("", "termite")
	check(err)
	syscall.Umask(0)

	ac := NewAttributeCache(
		func(n string) *FileAttr {
			return getattr(t, filepath.Join(dir, n))
		},
		func(n string) *os.FileInfo {
			return testStat(t, filepath.Join(dir, n))
		})
	err = ioutil.WriteFile(dir+"/file", []byte{42}, 0604)
	check(err)

	f := ac.Get("file")
	if f.Deletion() {
		t.Fatalf("Got deletion %v", f)
	}
	if f.Mode&0777 != 0604 {
		t.Fatalf("Got %o want %o", f.Mode&0777, 0604)
	}
	if !ac.Have("") {
		t.Fatalf("Must have parent too")
	}
	d := ac.GetDir("")
	if d.NameModeMap == nil || d.NameModeMap["file"] == 0 {
		t.Fatalf("root NameModeMap wrong %v", d.NameModeMap)
	}

	upd := FileAttr{
		Path:     "unknown/file",
		FileInfo: &os.FileInfo{Mode: fuse.S_IFLNK | 0666},
		Link:     "target",
	}

	ac.Update([]*FileAttr{&upd})
	if ac.Have("unknown/file") || ac.Have("unknown") {
		t.Fatalf("Should have ignored unknown directory")
	}

	// Make sure timestamps change.
	time.Sleep(15e6)
	err = ioutil.WriteFile(dir+"/other", []byte{43}, 0666)
	check(err)
	err = os.Chmod(dir+"/file", 0666)
	check(err)

	ac.Refresh("")

	d = ac.GetDir("")
	if d.NameModeMap["other"] == 0 {
		t.Fatalf("Should have 'other' in root %v", d)
	}
	f = ac.Get("file")
	if f.Mode&0777 != 0666 {
		t.Fatalf("Got %o , want 0666", f.Mode)
	}

}
<commit_msg>Use larger timeout on attrcache test.<commit_after>package termite

import (
	"github.com/hanwen/go-fuse/fuse"
	"log"
	"io/ioutil"
	"os"
	"path/filepath"
	"syscall"
	"testing"
	"time"
)

var _ = log.Printf

func testStat(t *testing.T, n string) *os.FileInfo {
	t.Logf("stat %q", n)
	f, _ := os.Lstat(n)
	return f
}

func getattr(t *testing.T, n string) *FileAttr {
	t.Logf("getattr %q", n)
	fi, _ := os.Lstat(n)
	a := FileAttr{
		FileInfo: fi,
	}
	if !a.Deletion() {
		a.ReadFromFs(n)
	}
	return &a
}

func TestAttrCacheNil(t *testing.T) {
	ac := NewAttributeCache(
		func(n string) *FileAttr {
		return nil
	},
		func(n string) *os.FileInfo {
		return nil
	})

	r := ac.Get("")
	if r == nil || !r.Deletion() {
		t.Errorf("should return deletion for error, got: %v", r)
	}
}

func TestAttrCache(t *testing.T) {
	dir, err := ioutil.TempDir("", "termite")
	check(err)
	syscall.Umask(0)

	ac := NewAttributeCache(
		func(n string) *FileAttr {
			return getattr(t, filepath.Join(dir, n))
		},
		func(n string) *os.FileInfo {
			return testStat(t, filepath.Join(dir, n))
		})
	err = ioutil.WriteFile(dir+"/file", []byte{42}, 0604)
	check(err)

	f := ac.Get("file")
	if f.Deletion() {
		t.Fatalf("Got deletion %v", f)
	}
	if f.Mode&0777 != 0604 {
		t.Fatalf("Got %o want %o", f.Mode&0777, 0604)
	}
	if !ac.Have("") {
		t.Fatalf("Must have parent too")
	}
	d := ac.GetDir("")
	if d.NameModeMap == nil || d.NameModeMap["file"] == 0 {
		t.Fatalf("root NameModeMap wrong %v", d.NameModeMap)
	}

	upd := FileAttr{
		Path:     "unknown/file",
		FileInfo: &os.FileInfo{Mode: fuse.S_IFLNK | 0666},
		Link:     "target",
	}

	ac.Update([]*FileAttr{&upd})
	if ac.Have("unknown/file") || ac.Have("unknown") {
		t.Fatalf("Should have ignored unknown directory")
	}

	// Make sure timestamps change.
	time.Sleep(150e6)
	err = ioutil.WriteFile(dir+"/other", []byte{43}, 0666)
	check(err)
	err = os.Chmod(dir+"/file", 0666)
	check(err)

	ac.Refresh("")

	d = ac.GetDir("")
	if d.NameModeMap["other"] == 0 {
		t.Fatalf("Should have 'other' in root %v", d)
	}
	f = ac.Get("file")
	if f.Mode&0777 != 0666 {
		t.Fatalf("Got %o , want 0666", f.Mode)
	}

}
<|endoftext|>


<commit_before>package atm

import (
	"os"
	"testing"
)

//create template directory and sub directories
func createDirs(t *testing.T) {
	dir, err := os.Getwd()
	if err != nil {
		t.Fatal(err)
	}

	if err := os.MkdirAll(dir+"/templates/atoms/fonts", os.ModeDir|os.ModePerm); err != nil {
		t.Fatal(err)
	}
	if err := os.MkdirAll(dir+"/templates/pages/front-page", os.ModeDir|os.ModePerm); err != nil {
		t.Fatal(err)
	}
}

func createTestTemplates(t *testing.T) {
	dir, err := os.Getwd()
	if err != nil {
		t.Fatal(err)
	}

	err = writeTemplateFile(dir+"/templates/top-level.html", ``)
	if err != nil {
		t.Fatal(err)
	}

	err = writeTemplateFile(dir+"/templates/none.none", ``)
	if err != nil {
		t.Fatal(err)
	}

	err = writeTemplateFile(dir+"/templates/atoms/atom-1.html", ``)
	if err != nil {
		t.Fatal(err)
	}

	err = writeTemplateFile(dir+"/templates/atoms/atom-2.tpl", ``)
	if err != nil {
		t.Fatal(err)
	}

	err = writeTemplateFile(dir+"/templates/atoms/fonts/font-1.html", ``)
	if err != nil {
		t.Fatal(err)
	}
}

func writeTemplateFile(path, contents string) error {
	templateFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		return err
	}
	defer templateFile.Close()
	_, err = templateFile.WriteString(contents)
	if err != nil {
		return err
	}
	return nil
}

func destroyAll(t *testing.T) {
	if err := os.RemoveAll("./templates"); err != nil {
		t.Fatal(err)
	}
}

func TestMeetsInterface(t *testing.T) {
	var _ Manager = New()
}

func TestErrorOnExecutingTemplate(t *testing.T) {
	var m Manager = New()

	err := m.ExecuteTemplate(os.Stdin, "testing", nil)

	if err == nil {
		t.Fatal("Expected error but got none.")
	}

	err = m.ExecuteTemplate(os.Stdin, "root", nil)

	if err == nil {
		t.Fatal("Expected error but got none.")
	}
}

func TestNoTemplatesAreFound(t *testing.T) {
	createDirs(t)
	defer destroyAll(t)

	var man Manager = New()
	man.AddDirectories("./templates")
	man.ParseTemplates()
	if len(man.Templates()) > 0 {
		t.Fatal("There were templates even though the directories were empty.")
	}
}

func TestDefaultTemplatesAreFound(t *testing.T) {
	createDirs(t)
	createTestTemplates(t)
	defer destroyAll(t)

	var man Manager = New()

	man.AddDirectories("./templates")
	man.ParseTemplates()
	if len(man.Templates()) != 4 {
		t.Fatalf("We expected 4 templates but had : %d", len(man.Templates()))
	}
}
<commit_msg>Finished merge with origin/master<commit_after>package atm

import (
	"os"
	"testing"
)

//create template directory and sub directories
func createDirs(t *testing.T) {
	dir, err := os.Getwd()
	if err != nil {
		t.Fatal(err)
	}
	if err := os.MkdirAll(dir+"/templates/atoms/fonts", os.ModeDir|os.ModePerm); err != nil {
		t.Fatal(err)
	}
	if err := os.MkdirAll(dir+"/templates/pages/front-page", os.ModeDir|os.ModePerm); err != nil {
		t.Fatal(err)
	}
}

func createTestTemplates(t *testing.T) {
	dir, err := os.Getwd()
	if err != nil {
		t.Fatal(err)
	}

	err = writeTemplateFile(dir+"/templates/top-level.html", ``)
	if err != nil {
		t.Fatal(err)
	}

	err = writeTemplateFile(dir+"/templates/none.none", ``)
	if err != nil {
		t.Fatal(err)
	}

	err = writeTemplateFile(dir+"/templates/atoms/atom-1.html", ``)
	if err != nil {
		t.Fatal(err)
	}

	err = writeTemplateFile(dir+"/templates/atoms/atom-2.tpl", ``)
	if err != nil {
		t.Fatal(err)
	}

	err = writeTemplateFile(dir+"/templates/atoms/fonts/font-1.html", ``)
	if err != nil {
		t.Fatal(err)
	}
}

func writeTemplateFile(path, contents string) error {
	templateFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		return err
	}
	defer templateFile.Close()
	_, err = templateFile.WriteString(contents)
	if err != nil {
		return err
	}
	return nil
}

func destroyAll(t *testing.T) {
	if err := os.RemoveAll("./templates"); err != nil {
		t.Fatal(err)
	}
}

func TestMeetsInterface(t *testing.T) {
	var _ Manager = New()
}

func TestErrorOnExecutingTemplate(t *testing.T) {
	var m Manager = New()

	err := m.ExecuteTemplate(os.Stdin, "testing", nil)

	if err == nil {
		t.Fatal("Expected error but got none.")
	}

	err = m.ExecuteTemplate(os.Stdin, "root", nil)

	if err == nil {
		t.Fatal("Expected error but got none.")
	}
}

func TestNoTemplatesAreFound(t *testing.T) {
	createDirs(t)
	defer destroyAll(t)

	var man Manager = New()
	man.AddDirectories("./templates")
	man.ParseTemplates()
	if len(man.Templates()) > 0 {
		t.Fatal("There were templates even though the directories were empty.")
	}
}

func TestDefaultTemplatesAreFound(t *testing.T) {
	createDirs(t)
	createTestTemplates(t)
	defer destroyAll(t)

	var man Manager = New()

	man.AddDirectories("./templates")
	man.ParseTemplates()
	if len(man.Templates()) != 4 {
		t.Fatalf("We expected 4 templates but had : %d", len(man.Templates()))
	}
}
<|endoftext|>


<commit_before>package v1

import (
	"net"

	"github.com/sapcc/kubernikus/pkg/api/models"
	"github.com/sapcc/kubernikus/pkg/util/ip"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

type Kluster struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata"`
	Spec              models.KlusterSpec   `json:"spec"`
	Status            models.KlusterStatus `json:"status,omitempty"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

type KlusterList struct {
	metav1.TypeMeta `json:",inline"`
	metav1.ListMeta `json:"metadata"`
	Items           []Kluster `json:"items"`
}

func (spec Kluster) Account() string {
	return spec.ObjectMeta.Labels["account"]
}

func (spec Kluster) ApiServiceIP() (net.IP, error) {
	_, ipnet, err := net.ParseCIDR(spec.Spec.ServiceCIDR)
	if err != nil {
		return nil, err
	}
	ip, err := ip.GetIndexedIP(ipnet, 1)
	if err != nil {
		return nil, err
	}
	return ip, nil

}

func (k *Kluster) NeedsFinalizer(finalizer string) bool {
	if k.ObjectMeta.DeletionTimestamp != nil {
		// already deleted. do not add another finalizer anymore
		return false
	}

	for _, f := range k.ObjectMeta.Finalizers {
		if f == finalizer {
			// Finalizer is already present, nothing to do
			return false
		}
	}

	return true
}

func (k *Kluster) HasFinalizer(finalizer string) bool {
	if k.ObjectMeta.DeletionTimestamp == nil {
		// not deleted. do not remove finalizers at this time
		return false
	}

	for _, f := range k.ObjectMeta.Finalizers {
		if f == finalizer {
			// Finalizer is already present
			return true
		}
	}

	return false
}

func (k *Kluster) AddFinalizer(finalizer string) {
	if k.NeedsFinalizer(finalizer) {
		k.Finalizers = append(k.Finalizers, finalizer)
	}
}

func (k *Kluster) RemoveFinalizer(finalizer string) {
	if k.HasFinalizer(finalizer) {
		for i, f := range k.Finalizers {
			if f == finalizer {
				k.Finalizers = append(k.Finalizers[:i], k.Finalizers[i+1:]...)
				break
			}
		}
	}
}

func (k *Kluster) Disabled() bool {
	return k.Status.MigrationsPending
}
<commit_msg>Improve error message<commit_after>package v1

import (
	"fmt"
	"net"

	"github.com/sapcc/kubernikus/pkg/api/models"
	"github.com/sapcc/kubernikus/pkg/util/ip"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

type Kluster struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata"`
	Spec              models.KlusterSpec   `json:"spec"`
	Status            models.KlusterStatus `json:"status,omitempty"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

type KlusterList struct {
	metav1.TypeMeta `json:",inline"`
	metav1.ListMeta `json:"metadata"`
	Items           []Kluster `json:"items"`
}

func (spec Kluster) Account() string {
	return spec.ObjectMeta.Labels["account"]
}

func (spec Kluster) ApiServiceIP() (net.IP, error) {
	_, ipnet, err := net.ParseCIDR(spec.Spec.ServiceCIDR)
	if err != nil {
		return nil, fmt.Errorf("Failed to parse service CIDR: %s", err)
	}
	ip, err := ip.GetIndexedIP(ipnet, 1)
	if err != nil {
		return nil, err
	}
	return ip, nil

}

func (k *Kluster) NeedsFinalizer(finalizer string) bool {
	if k.ObjectMeta.DeletionTimestamp != nil {
		// already deleted. do not add another finalizer anymore
		return false
	}

	for _, f := range k.ObjectMeta.Finalizers {
		if f == finalizer {
			// Finalizer is already present, nothing to do
			return false
		}
	}

	return true
}

func (k *Kluster) HasFinalizer(finalizer string) bool {
	if k.ObjectMeta.DeletionTimestamp == nil {
		// not deleted. do not remove finalizers at this time
		return false
	}

	for _, f := range k.ObjectMeta.Finalizers {
		if f == finalizer {
			// Finalizer is already present
			return true
		}
	}

	return false
}

func (k *Kluster) AddFinalizer(finalizer string) {
	if k.NeedsFinalizer(finalizer) {
		k.Finalizers = append(k.Finalizers, finalizer)
	}
}

func (k *Kluster) RemoveFinalizer(finalizer string) {
	if k.HasFinalizer(finalizer) {
		for i, f := range k.Finalizers {
			if f == finalizer {
				k.Finalizers = append(k.Finalizers[:i], k.Finalizers[i+1:]...)
				break
			}
		}
	}
}

func (k *Kluster) Disabled() bool {
	return k.Status.MigrationsPending
}
<|endoftext|>