<commit_before>package zwave

import "fmt"

import "github.com/bjyoungblood/gozw/zwave/commandclass"

type Manager struct {
	session *SessionLayer

	ApiVersion     string
	ApiLibraryType string

	HomeId uint32
	NodeId byte

	Version                 byte
	ApiType                 string
	TimerFunctionsSupported bool
	IsPrimaryController     bool
	ApplicationVersion      byte
	ApplicationRevision     byte
	SupportedFunctions      []byte

	nodeList []uint8
	Nodes    map[uint8]*Node
}

func NewManager(session *SessionLayer) *Manager {
	manager := &Manager{
		session: session,
		Nodes:   map[uint8]*Node{},
	}

	session.manager = manager

	manager.init()

	return manager
}

func (m *Manager) init() {
	version, err := m.GetVersion()
	if err != nil {
		panic(err)
	}

	m.ApiVersion = version.ApiVersion
	m.ApiLibraryType = version.GetLibraryTypeString()

	ids, err := m.GetHomeId()
	if err != nil {
		panic(err)
	}

	m.HomeId = ids.HomeId
	m.NodeId = ids.NodeId

	appInfo, err := m.GetAppInfo()
	if err != nil {
		panic(err)
	}

	m.Version = appInfo.Version
	m.ApiType = appInfo.GetApiType()
	m.TimerFunctionsSupported = appInfo.TimerFunctionsSupported()
	m.IsPrimaryController = appInfo.IsPrimaryController()
	m.nodeList = appInfo.GetNodeIds()

	serialApi, err := m.GetSerialApiCapabilities()
	if err != nil {
		panic(err)
	}

	m.ApplicationVersion = serialApi.ApplicationVersion
	m.ApplicationRevision = serialApi.ApplicationRevision
	m.SupportedFunctions = serialApi.GetSupportedFunctions()

	m.loadNodes()

	go m.handleUnsolicitedFrames()

	m.session.SetSerialAPIReady(true)
}

func (m *Manager) Close() {
	m.session.SetSerialAPIReady(false)
}

func (m *Manager) SetApplicationNodeInformation() {
	m.session.ApplicationNodeInformation(
		ApplicationNodeInfoListening|ApplicationFreqListeningMode250ms|ApplicationNodeInfoOptionalFunctionality,
		GenericTypeGenericController,
		SpecificTypePortableSceneController,
		[]uint8{
			commandclass.CommandClassBasic,
		},
	)
}

func (m *Manager) FactoryReset() {
	m.session.SetDefault()
}

func (m *Manager) AddNode() {
	node, err := m.session.AddNodeToNetwork()
	if err != nil {
		fmt.Println(err)
		return
	}

	m.Nodes[node.NodeId] = node
	fmt.Println(node.String())
}

func (m *Manager) includeSecureNode(node *Node) {
	// fmt.Println(node.NodeId)
	// m.SendData(node.NodeId, commandclass.NewSecuritySchemeGet())
	// schemeReport := m.session.WaitForFrame()
	// fmt.Println(schemeReport.Payload)
}

func (m *Manager) RemoveNode() {
	node, err := m.session.AddNodeToNetwork()
	if err != nil {
		fmt.Println(err)
		return
	}

	delete(m.Nodes, node.NodeId)
	fmt.Println(node.String())
}

func (m *Manager) GetHomeId() (*MemoryGetIdResponse, error) {
	return m.session.MemoryGetId()
}

func (m *Manager) GetAppInfo() (*NodeListResponse, error) {
	return m.session.GetInitAppData()
}

func (m *Manager) GetSerialApiCapabilities() (*SerialApiCapabilitiesResponse, error) {
	return m.session.GetSerialApiCapabilities()
}

func (m *Manager) GetVersion() (*VersionResponse, error) {
	return m.session.GetVersion()
}

func (m *Manager) SendData(nodeId uint8, data []byte) {
	resp, err := m.session.SendData(nodeId, data)
	fmt.Println(resp, err)
	if err != nil {
		fmt.Println("senddata error", resp)
	} else {
		fmt.Println("senddata reply", resp.Payload)
	}
}

func (m *Manager) loadNodes() {
	for _, nodeId := range m.nodeList {
		nodeInfo, _ := m.session.GetNodeProtocolInfo(nodeId)
		node := NewNode(m, nodeId)

		node.setFromNodeProtocolInfo(nodeInfo)

		m.Nodes[nodeId] = node
	}
}

func (m *Manager) handleUnsolicitedFrames() {
	for frame := range m.session.UnsolicitedFrames {
		switch frame.Payload[0] {
		case FnApplicationCommandHandlerBridge:
			cmd := ParseApplicationCommandHandlerBridge(frame.Payload)
			if cmd.CmdLength > 0 {
				fmt.Printf("Got %s: %v", commandclass.GetCommandClassString(cmd.CommandData[0]), cmd.CommandData)
			} else {
				fmt.Println("wat", cmd)
			}
		default:
			fmt.Println("Received unsolicited frame:", frame)
		}
	}
}
<commit_msg>fix node removal<commit_after>package zwave

import "fmt"

import "github.com/bjyoungblood/gozw/zwave/commandclass"

type Manager struct {
	session *SessionLayer

	ApiVersion     string
	ApiLibraryType string

	HomeId uint32
	NodeId byte

	Version                 byte
	ApiType                 string
	TimerFunctionsSupported bool
	IsPrimaryController     bool
	ApplicationVersion      byte
	ApplicationRevision     byte
	SupportedFunctions      []byte

	nodeList []uint8
	Nodes    map[uint8]*Node
}

func NewManager(session *SessionLayer) *Manager {
	manager := &Manager{
		session: session,
		Nodes:   map[uint8]*Node{},
	}

	session.manager = manager

	manager.init()

	return manager
}

func (m *Manager) init() {
	version, err := m.GetVersion()
	if err != nil {
		panic(err)
	}

	m.ApiVersion = version.ApiVersion
	m.ApiLibraryType = version.GetLibraryTypeString()

	ids, err := m.GetHomeId()
	if err != nil {
		panic(err)
	}

	m.HomeId = ids.HomeId
	m.NodeId = ids.NodeId

	appInfo, err := m.GetAppInfo()
	if err != nil {
		panic(err)
	}

	m.Version = appInfo.Version
	m.ApiType = appInfo.GetApiType()
	m.TimerFunctionsSupported = appInfo.TimerFunctionsSupported()
	m.IsPrimaryController = appInfo.IsPrimaryController()
	m.nodeList = appInfo.GetNodeIds()

	serialApi, err := m.GetSerialApiCapabilities()
	if err != nil {
		panic(err)
	}

	m.ApplicationVersion = serialApi.ApplicationVersion
	m.ApplicationRevision = serialApi.ApplicationRevision
	m.SupportedFunctions = serialApi.GetSupportedFunctions()

	m.loadNodes()

	go m.handleUnsolicitedFrames()

	m.session.SetSerialAPIReady(true)
}

func (m *Manager) Close() {
	m.session.SetSerialAPIReady(false)
}

func (m *Manager) SetApplicationNodeInformation() {
	m.session.ApplicationNodeInformation(
		ApplicationNodeInfoListening|ApplicationFreqListeningMode250ms|ApplicationNodeInfoOptionalFunctionality,
		GenericTypeGenericController,
		SpecificTypePortableSceneController,
		[]uint8{
			commandclass.CommandClassBasic,
		},
	)
}

func (m *Manager) FactoryReset() {
	m.session.SetDefault()
}

func (m *Manager) AddNode() {
	node, err := m.session.AddNodeToNetwork()
	if err != nil {
		fmt.Println(err)
		return
	}

	m.Nodes[node.NodeId] = node
	fmt.Println(node.String())
}

func (m *Manager) includeSecureNode(node *Node) {
	// fmt.Println(node.NodeId)
	// m.SendData(node.NodeId, commandclass.NewSecuritySchemeGet())
	// schemeReport := m.session.WaitForFrame()
	// fmt.Println(schemeReport.Payload)
}

func (m *Manager) RemoveNode() {
	node, err := m.session.RemoveNodeFromNetwork()
	if err != nil {
		fmt.Println(err)
		return
	}

	delete(m.Nodes, node.NodeId)
	fmt.Println(node.String())
}

func (m *Manager) GetHomeId() (*MemoryGetIdResponse, error) {
	return m.session.MemoryGetId()
}

func (m *Manager) GetAppInfo() (*NodeListResponse, error) {
	return m.session.GetInitAppData()
}

func (m *Manager) GetSerialApiCapabilities() (*SerialApiCapabilitiesResponse, error) {
	return m.session.GetSerialApiCapabilities()
}

func (m *Manager) GetVersion() (*VersionResponse, error) {
	return m.session.GetVersion()
}

func (m *Manager) SendData(nodeId uint8, data []byte) {
	resp, err := m.session.SendData(nodeId, data)
	fmt.Println(resp, err)
	if err != nil {
		fmt.Println("senddata error", resp)
	} else {
		fmt.Println("senddata reply", resp.Payload)
	}
}

func (m *Manager) loadNodes() {
	for _, nodeId := range m.nodeList {
		nodeInfo, _ := m.session.GetNodeProtocolInfo(nodeId)
		node := NewNode(m, nodeId)

		node.setFromNodeProtocolInfo(nodeInfo)

		m.Nodes[nodeId] = node
	}
}

func (m *Manager) handleUnsolicitedFrames() {
	for frame := range m.session.UnsolicitedFrames {
		switch frame.Payload[0] {
		case FnApplicationCommandHandlerBridge:
			cmd := ParseApplicationCommandHandlerBridge(frame.Payload)
			if cmd.CmdLength > 0 {
				fmt.Printf("Got %s: %v", commandclass.GetCommandClassString(cmd.CommandData[0]), cmd.CommandData)
			} else {
				fmt.Println("wat", cmd)
			}
		default:
			fmt.Println("Received unsolicited frame:", frame)
		}
	}
}
<|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.
*/

package model

import (
	"fmt"
	"path/filepath"
	"strconv"
	"strings"

	"k8s.io/kops/pkg/apis/kops"
	"k8s.io/kops/pkg/configbuilder"
	"k8s.io/kops/pkg/flagbuilder"
	"k8s.io/kops/pkg/k8scodecs"
	"k8s.io/kops/pkg/kubemanifest"
	"k8s.io/kops/pkg/rbac"
	"k8s.io/kops/upup/pkg/fi"
	"k8s.io/kops/upup/pkg/fi/nodeup/nodetasks"
	"k8s.io/kops/util/pkg/architectures"
	"k8s.io/kops/util/pkg/proxy"

	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
)

// ClientConnectionConfig is used by kube-scheduler to talk to the api server
type ClientConnectionConfig struct {
	Burst      int32    `json:"burst,omitempty"`
	Kubeconfig string   `json:"kubeconfig"`
	QPS        *float64 `json:"qps,omitempty"`
}

// SchedulerConfig is used to generate the config file
type SchedulerConfig struct {
	APIVersion       string                 `json:"apiVersion"`
	Kind             string                 `json:"kind"`
	ClientConnection ClientConnectionConfig `json:"clientConnection,omitempty"`
}

// KubeSchedulerBuilder install kube-scheduler
type KubeSchedulerBuilder struct {
	*NodeupModelContext
}

var _ fi.ModelBuilder = &KubeSchedulerBuilder{}

const defaultKubeConfig = "/var/lib/kube-scheduler/kubeconfig"

// Build is responsible for building the manifest for the kube-scheduler
func (b *KubeSchedulerBuilder) Build(c *fi.ModelBuilderContext) error {
	if !b.IsMaster {
		return nil
	}

	kubeScheduler := *b.Cluster.Spec.KubeScheduler

	if err := b.writeServerCertificate(c, &kubeScheduler); err != nil {
		return err
	}

	{
		pod, err := b.buildPod(&kubeScheduler)
		if err != nil {
			return fmt.Errorf("error building kube-scheduler pod: %v", err)
		}

		manifest, err := k8scodecs.ToVersionedYaml(pod)
		if err != nil {
			return fmt.Errorf("error marshaling pod to yaml: %v", err)
		}

		c.AddTask(&nodetasks.File{
			Path:     "/etc/kubernetes/manifests/kube-scheduler.manifest",
			Contents: fi.NewBytesResource(manifest),
			Type:     nodetasks.FileType_File,
		})
	}

	{
		kubeconfig := b.BuildIssuedKubeconfig("kube-scheduler", nodetasks.PKIXName{CommonName: rbac.KubeScheduler}, c)

		c.AddTask(&nodetasks.File{
			Path:     "/var/lib/kube-scheduler/kubeconfig",
			Contents: kubeconfig,
			Type:     nodetasks.FileType_File,
			Mode:     s("0400"),
		})
	}
	{
		var config *SchedulerConfig
		if b.IsKubernetesGTE("1.19") {
			config = NewSchedulerConfig("kubescheduler.config.k8s.io/v1beta1")
		} else {
			config = NewSchedulerConfig("kubescheduler.config.k8s.io/v1alpha2")
		}

		manifest, err := configbuilder.BuildConfigYaml(&kubeScheduler, config)
		if err != nil {
			return err
		}

		c.AddTask(&nodetasks.File{
			Path:     "/var/lib/kube-scheduler/config.yaml",
			Contents: fi.NewBytesResource(manifest),
			Type:     nodetasks.FileType_File,
			Mode:     s("0400"),
		})
	}

	{
		c.AddTask(&nodetasks.File{
			Path:        "/var/log/kube-scheduler.log",
			Contents:    fi.NewStringResource(""),
			Type:        nodetasks.FileType_File,
			Mode:        s("0400"),
			IfNotExists: true,
		})
	}

	return nil
}

// NewSchedulerConfig initializes a new kube-scheduler config file
func NewSchedulerConfig(apiVersion string) *SchedulerConfig {
	schedConfig := new(SchedulerConfig)
	schedConfig.APIVersion = apiVersion
	schedConfig.Kind = "KubeSchedulerConfiguration"
	schedConfig.ClientConnection = ClientConnectionConfig{}
	schedConfig.ClientConnection.Kubeconfig = defaultKubeConfig
	return schedConfig
}

func (b *KubeSchedulerBuilder) writeServerCertificate(c *fi.ModelBuilderContext, kubeScheduler *kops.KubeSchedulerConfig) error {
	pathSrvScheduler := filepath.Join(b.PathSrvKubernetes(), "kube-scheduler")

	if kubeScheduler.TLSCertFile == nil {
		alternateNames := []string{
			"kube-scheduler.kube-system.svc." + b.Cluster.Spec.ClusterDNSDomain,
		}

		issueCert := &nodetasks.IssueCert{
			Name:           "kube-scheduler-server",
			Signer:         fi.CertificateIDCA,
			KeypairID:      b.NodeupConfig.KeypairIDs[fi.CertificateIDCA],
			Type:           "server",
			Subject:        nodetasks.PKIXName{CommonName: "kube-scheduler"},
			AlternateNames: alternateNames,
		}

		c.AddTask(issueCert)
		err := issueCert.AddFileTasks(c, pathSrvScheduler, "server", "", nil)
		if err != nil {
			return err
		}

		kubeScheduler.TLSCertFile = fi.String(filepath.Join(pathSrvScheduler, "server.crt"))
		kubeScheduler.TLSPrivateKeyFile = filepath.Join(pathSrvScheduler, "server.key")
	}

	return nil
}

// buildPod is responsible for constructing the pod specification
func (b *KubeSchedulerBuilder) buildPod(kubeScheduler *kops.KubeSchedulerConfig) (*v1.Pod, error) {
	pathSrvScheduler := filepath.Join(b.PathSrvKubernetes(), "kube-scheduler")

	flags, err := flagbuilder.BuildFlagsList(kubeScheduler)
	if err != nil {
		return nil, fmt.Errorf("error building kube-scheduler flags: %v", err)
	}

	flags = append(flags, "--config="+"/var/lib/kube-scheduler/config.yaml")

	// Add kubeconfig flags
	for _, flag := range []string{"authentication-", "authorization-"} {
		flags = append(flags, "--"+flag+"kubeconfig="+defaultKubeConfig)
	}

	if kubeScheduler.UsePolicyConfigMap != nil {
		flags = append(flags, "--policy-configmap=scheduler-policy", "--policy-configmap-namespace=kube-system")
	}

	pod := &v1.Pod{
		TypeMeta: metav1.TypeMeta{
			APIVersion: "v1",
			Kind:       "Pod",
		},
		ObjectMeta: metav1.ObjectMeta{
			Name:      "kube-scheduler",
			Namespace: "kube-system",
			Labels: map[string]string{
				"k8s-app": "kube-scheduler",
			},
		},
		Spec: v1.PodSpec{
			HostNetwork: true,
		},
	}

	image := kubeScheduler.Image
	if b.Architecture != architectures.ArchitectureAmd64 {
		image = strings.Replace(image, "-amd64", "-"+string(b.Architecture), 1)
	}

	healthAction := &v1.HTTPGetAction{
		Host: "127.0.0.1",
		Path: "/healthz",
		Port: intstr.FromInt(10251),
	}
	if b.IsKubernetesGTE("1.23") {
		healthAction.Port = intstr.FromInt(10259)
		healthAction.Scheme = v1.URISchemeHTTPS
	}

	container := &v1.Container{
		Name:  "kube-scheduler",
		Image: image,
		Env:   proxy.GetProxyEnvVars(b.Cluster.Spec.EgressProxy),
		LivenessProbe: &v1.Probe{
			Handler:             v1.Handler{HTTPGet: healthAction},
			InitialDelaySeconds: 15,
			TimeoutSeconds:      15,
		},
		Resources: v1.ResourceRequirements{
			Requests: v1.ResourceList{
				v1.ResourceCPU: resource.MustParse("100m"),
			},
		},
	}
	addHostPathMapping(pod, container, "varlibkubescheduler", "/var/lib/kube-scheduler")
	addHostPathMapping(pod, container, "srvscheduler", pathSrvScheduler)

	// Log both to docker and to the logfile
	addHostPathMapping(pod, container, "logfile", "/var/log/kube-scheduler.log").ReadOnly = false
	// We use lighter containers that don't include shells
	// But they have richer logging support via klog
	container.Command = []string{"/usr/local/bin/kube-scheduler"}
	container.Args = append(
		sortedStrings(flags),
		"--logtostderr=false", //https://github.com/kubernetes/klog/issues/60
		"--alsologtostderr",
		"--log-file=/var/log/kube-scheduler.log")

	if kubeScheduler.MaxPersistentVolumes != nil {
		maxPDV := v1.EnvVar{
			Name:  "KUBE_MAX_PD_VOLS", // https://kubernetes.io/docs/concepts/storage/storage-limits/
			Value: strconv.Itoa(int(*kubeScheduler.MaxPersistentVolumes)),
		}
		container.Env = append(container.Env, maxPDV)
	}

	pod.Spec.Containers = append(pod.Spec.Containers, *container)

	kubemanifest.MarkPodAsCritical(pod)
	kubemanifest.MarkPodAsClusterCritical(pod)

	return pod, nil
}
<commit_msg>Add kubescheduler.config.k8s.io/v1beta2 for k8s 1.22+<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.
*/

package model

import (
	"fmt"
	"path/filepath"
	"strconv"
	"strings"

	"k8s.io/kops/pkg/apis/kops"
	"k8s.io/kops/pkg/configbuilder"
	"k8s.io/kops/pkg/flagbuilder"
	"k8s.io/kops/pkg/k8scodecs"
	"k8s.io/kops/pkg/kubemanifest"
	"k8s.io/kops/pkg/rbac"
	"k8s.io/kops/upup/pkg/fi"
	"k8s.io/kops/upup/pkg/fi/nodeup/nodetasks"
	"k8s.io/kops/util/pkg/architectures"
	"k8s.io/kops/util/pkg/proxy"

	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
)

// ClientConnectionConfig is used by kube-scheduler to talk to the api server
type ClientConnectionConfig struct {
	Burst      int32    `json:"burst,omitempty"`
	Kubeconfig string   `json:"kubeconfig"`
	QPS        *float64 `json:"qps,omitempty"`
}

// SchedulerConfig is used to generate the config file
type SchedulerConfig struct {
	APIVersion       string                 `json:"apiVersion"`
	Kind             string                 `json:"kind"`
	ClientConnection ClientConnectionConfig `json:"clientConnection,omitempty"`
}

// KubeSchedulerBuilder install kube-scheduler
type KubeSchedulerBuilder struct {
	*NodeupModelContext
}

var _ fi.ModelBuilder = &KubeSchedulerBuilder{}

const defaultKubeConfig = "/var/lib/kube-scheduler/kubeconfig"

// Build is responsible for building the manifest for the kube-scheduler
func (b *KubeSchedulerBuilder) Build(c *fi.ModelBuilderContext) error {
	if !b.IsMaster {
		return nil
	}

	kubeScheduler := *b.Cluster.Spec.KubeScheduler

	if err := b.writeServerCertificate(c, &kubeScheduler); err != nil {
		return err
	}

	{
		pod, err := b.buildPod(&kubeScheduler)
		if err != nil {
			return fmt.Errorf("error building kube-scheduler pod: %v", err)
		}

		manifest, err := k8scodecs.ToVersionedYaml(pod)
		if err != nil {
			return fmt.Errorf("error marshaling pod to yaml: %v", err)
		}

		c.AddTask(&nodetasks.File{
			Path:     "/etc/kubernetes/manifests/kube-scheduler.manifest",
			Contents: fi.NewBytesResource(manifest),
			Type:     nodetasks.FileType_File,
		})
	}

	{
		kubeconfig := b.BuildIssuedKubeconfig("kube-scheduler", nodetasks.PKIXName{CommonName: rbac.KubeScheduler}, c)

		c.AddTask(&nodetasks.File{
			Path:     "/var/lib/kube-scheduler/kubeconfig",
			Contents: kubeconfig,
			Type:     nodetasks.FileType_File,
			Mode:     s("0400"),
		})
	}
	{
		var config *SchedulerConfig
		if b.IsKubernetesGTE("1.22") {
			config = NewSchedulerConfig("kubescheduler.config.k8s.io/v1beta2")
		} else if b.IsKubernetesGTE("1.19") {
			config = NewSchedulerConfig("kubescheduler.config.k8s.io/v1beta1")
		} else {
			config = NewSchedulerConfig("kubescheduler.config.k8s.io/v1alpha2")
		}

		manifest, err := configbuilder.BuildConfigYaml(&kubeScheduler, config)
		if err != nil {
			return err
		}

		c.AddTask(&nodetasks.File{
			Path:     "/var/lib/kube-scheduler/config.yaml",
			Contents: fi.NewBytesResource(manifest),
			Type:     nodetasks.FileType_File,
			Mode:     s("0400"),
		})
	}

	{
		c.AddTask(&nodetasks.File{
			Path:        "/var/log/kube-scheduler.log",
			Contents:    fi.NewStringResource(""),
			Type:        nodetasks.FileType_File,
			Mode:        s("0400"),
			IfNotExists: true,
		})
	}

	return nil
}

// NewSchedulerConfig initializes a new kube-scheduler config file
func NewSchedulerConfig(apiVersion string) *SchedulerConfig {
	schedConfig := new(SchedulerConfig)
	schedConfig.APIVersion = apiVersion
	schedConfig.Kind = "KubeSchedulerConfiguration"
	schedConfig.ClientConnection = ClientConnectionConfig{}
	schedConfig.ClientConnection.Kubeconfig = defaultKubeConfig
	return schedConfig
}

func (b *KubeSchedulerBuilder) writeServerCertificate(c *fi.ModelBuilderContext, kubeScheduler *kops.KubeSchedulerConfig) error {
	pathSrvScheduler := filepath.Join(b.PathSrvKubernetes(), "kube-scheduler")

	if kubeScheduler.TLSCertFile == nil {
		alternateNames := []string{
			"kube-scheduler.kube-system.svc." + b.Cluster.Spec.ClusterDNSDomain,
		}

		issueCert := &nodetasks.IssueCert{
			Name:           "kube-scheduler-server",
			Signer:         fi.CertificateIDCA,
			KeypairID:      b.NodeupConfig.KeypairIDs[fi.CertificateIDCA],
			Type:           "server",
			Subject:        nodetasks.PKIXName{CommonName: "kube-scheduler"},
			AlternateNames: alternateNames,
		}

		c.AddTask(issueCert)
		err := issueCert.AddFileTasks(c, pathSrvScheduler, "server", "", nil)
		if err != nil {
			return err
		}

		kubeScheduler.TLSCertFile = fi.String(filepath.Join(pathSrvScheduler, "server.crt"))
		kubeScheduler.TLSPrivateKeyFile = filepath.Join(pathSrvScheduler, "server.key")
	}

	return nil
}

// buildPod is responsible for constructing the pod specification
func (b *KubeSchedulerBuilder) buildPod(kubeScheduler *kops.KubeSchedulerConfig) (*v1.Pod, error) {
	pathSrvScheduler := filepath.Join(b.PathSrvKubernetes(), "kube-scheduler")

	flags, err := flagbuilder.BuildFlagsList(kubeScheduler)
	if err != nil {
		return nil, fmt.Errorf("error building kube-scheduler flags: %v", err)
	}

	flags = append(flags, "--config="+"/var/lib/kube-scheduler/config.yaml")

	// Add kubeconfig flags
	for _, flag := range []string{"authentication-", "authorization-"} {
		flags = append(flags, "--"+flag+"kubeconfig="+defaultKubeConfig)
	}

	if kubeScheduler.UsePolicyConfigMap != nil {
		flags = append(flags, "--policy-configmap=scheduler-policy", "--policy-configmap-namespace=kube-system")
	}

	pod := &v1.Pod{
		TypeMeta: metav1.TypeMeta{
			APIVersion: "v1",
			Kind:       "Pod",
		},
		ObjectMeta: metav1.ObjectMeta{
			Name:      "kube-scheduler",
			Namespace: "kube-system",
			Labels: map[string]string{
				"k8s-app": "kube-scheduler",
			},
		},
		Spec: v1.PodSpec{
			HostNetwork: true,
		},
	}

	image := kubeScheduler.Image
	if b.Architecture != architectures.ArchitectureAmd64 {
		image = strings.Replace(image, "-amd64", "-"+string(b.Architecture), 1)
	}

	healthAction := &v1.HTTPGetAction{
		Host: "127.0.0.1",
		Path: "/healthz",
		Port: intstr.FromInt(10251),
	}
	if b.IsKubernetesGTE("1.23") {
		healthAction.Port = intstr.FromInt(10259)
		healthAction.Scheme = v1.URISchemeHTTPS
	}

	container := &v1.Container{
		Name:  "kube-scheduler",
		Image: image,
		Env:   proxy.GetProxyEnvVars(b.Cluster.Spec.EgressProxy),
		LivenessProbe: &v1.Probe{
			Handler:             v1.Handler{HTTPGet: healthAction},
			InitialDelaySeconds: 15,
			TimeoutSeconds:      15,
		},
		Resources: v1.ResourceRequirements{
			Requests: v1.ResourceList{
				v1.ResourceCPU: resource.MustParse("100m"),
			},
		},
	}
	addHostPathMapping(pod, container, "varlibkubescheduler", "/var/lib/kube-scheduler")
	addHostPathMapping(pod, container, "srvscheduler", pathSrvScheduler)

	// Log both to docker and to the logfile
	addHostPathMapping(pod, container, "logfile", "/var/log/kube-scheduler.log").ReadOnly = false
	// We use lighter containers that don't include shells
	// But they have richer logging support via klog
	container.Command = []string{"/usr/local/bin/kube-scheduler"}
	container.Args = append(
		sortedStrings(flags),
		"--logtostderr=false", //https://github.com/kubernetes/klog/issues/60
		"--alsologtostderr",
		"--log-file=/var/log/kube-scheduler.log")

	if kubeScheduler.MaxPersistentVolumes != nil {
		maxPDV := v1.EnvVar{
			Name:  "KUBE_MAX_PD_VOLS", // https://kubernetes.io/docs/concepts/storage/storage-limits/
			Value: strconv.Itoa(int(*kubeScheduler.MaxPersistentVolumes)),
		}
		container.Env = append(container.Env, maxPDV)
	}

	pod.Spec.Containers = append(pod.Spec.Containers, *container)

	kubemanifest.MarkPodAsCritical(pod)
	kubemanifest.MarkPodAsClusterCritical(pod)

	return pod, nil
}
<|endoftext|>


<commit_before>package easyvk

import (
	"bytes"
	"mime/multipart"
	"os"
	"net/http"
	"io/ioutil"
	"io"
	"encoding/json"
)

// An Upload describes a set of methods
// that helps with update to VK servers.
type Upload struct{}

// A PhotoWall describes an info
// about uploaded photo.
type PhotoWall struct {
	Server int `json:"server"`
	Photo  string `json:"photo"`
	Hash   string `json:"hash"`
}

// PhotoWall upload file (on filePath) to given url.
// Return info about uploaded photo.
func (u *Upload) PhotoWall(url, filePath string) (PhotoWall, error) {
	bodyBuf := &bytes.Buffer{}
	bodyWriter := multipart.NewWriter(bodyBuf)

	fileWriter, err := bodyWriter.CreateFormFile("photo", filePath)
	if err != nil {
		return PhotoWall{}, err
	}

	fh, err := os.Open(filePath)
	if err != nil {
		return PhotoWall{}, err
	}

	_, err = io.Copy(fileWriter, fh)
	if err != nil {
		return PhotoWall{}, err
	}

	contentType := bodyWriter.FormDataContentType()
	bodyWriter.Close()

	resp, err := http.Post(url, contentType, bodyBuf)
	if err != nil {
		return PhotoWall{}, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return PhotoWall{}, err
	}

	var uploaded PhotoWall
	err = json.Unmarshal(body, &uploaded)
	if err != nil {
		return PhotoWall{}, err
	}

	return uploaded, nil
}
<commit_msg>Upload responses renaming<commit_after>package easyvk

import (
	"bytes"
	"mime/multipart"
	"os"
	"net/http"
	"io/ioutil"
	"io"
	"encoding/json"
)

// An Upload describes a set of methods
// that helps with update to VK servers.
type Upload struct{}

// A UploadPhotoWallResponse describes an info
// about uploaded photo.
type UploadPhotoWallResponse struct {
	Server int `json:"server"`
	Photo  string `json:"photo"`
	Hash   string `json:"hash"`
}

// PhotoWall upload file (on filePath) to given url.
// Return info about uploaded photo.
func (u *Upload) PhotoWall(url, filePath string) (UploadPhotoWallResponse, error) {
	bodyBuf := &bytes.Buffer{}
	bodyWriter := multipart.NewWriter(bodyBuf)

	fileWriter, err := bodyWriter.CreateFormFile("photo", filePath)
	if err != nil {
		return UploadPhotoWallResponse{}, err
	}

	fh, err := os.Open(filePath)
	if err != nil {
		return UploadPhotoWallResponse{}, err
	}

	_, err = io.Copy(fileWriter, fh)
	if err != nil {
		return UploadPhotoWallResponse{}, err
	}

	contentType := bodyWriter.FormDataContentType()
	bodyWriter.Close()

	resp, err := http.Post(url, contentType, bodyBuf)
	if err != nil {
		return UploadPhotoWallResponse{}, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return UploadPhotoWallResponse{}, err
	}

	var uploaded UploadPhotoWallResponse
	err = json.Unmarshal(body, &uploaded)
	if err != nil {
		return UploadPhotoWallResponse{}, err
	}

	return uploaded, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strings"

	"github.com/aerogo/flow"
	"github.com/animenotifier/arn"
	"github.com/fatih/color"
)

func main() {
	color.Yellow("Updating search index")

	flow.Parallel(updateAnimeIndex, updateUserIndex)

	color.Green("Finished.")
}

func updateAnimeIndex() {
	animeSearchIndex := arn.NewSearchIndex()

	// Anime
	animeStream, err := arn.StreamAnime()

	if err != nil {
		panic(err)
	}

	for anime := range animeStream {
		if anime.Title.Romaji != "" {
			animeSearchIndex.TextToID[strings.ToLower(anime.Title.Romaji)] = anime.ID
		}

		if anime.Title.English != "" {
			animeSearchIndex.TextToID[strings.ToLower(anime.Title.English)] = anime.ID
		}

		// Make sure we only include Japanese titles that actually contain unicode letters
		// because otherwise they might overlap with the English titles.
		if anime.Title.Japanese != "" && arn.ContainsUnicodeLetters(anime.Title.Japanese) {
			animeSearchIndex.TextToID[strings.ToLower(anime.Title.Japanese)] = anime.ID
		}

		if anime.Title.Canonical != "" {
			animeSearchIndex.TextToID[strings.ToLower(anime.Title.Canonical)] = anime.ID
		}

		for _, synonym := range anime.Title.Synonyms {
			synonym = strings.ToLower(synonym)

			if synonym != "" && len(synonym) <= 10 {
				animeSearchIndex.TextToID[synonym] = anime.ID
			}
		}
	}

	fmt.Println(len(animeSearchIndex.TextToID), "anime titles")

	// Save in database
	err = arn.DB.Set("SearchIndex", "Anime", animeSearchIndex)

	if err != nil {
		panic(err)
	}
}

func updateUserIndex() {
	userSearchIndex := arn.NewSearchIndex()

	// Users
	userStream, err := arn.StreamUsers()

	if err != nil {
		panic(err)
	}

	for user := range userStream {
		if user.IsActive() && user.Nick != "" {
			userSearchIndex.TextToID[strings.ToLower(user.Nick)] = user.ID
		}
	}

	fmt.Println(len(userSearchIndex.TextToID), "user names")

	// Save in database
	err = arn.DB.Set("SearchIndex", "User", userSearchIndex)

	if err != nil {
		panic(err)
	}
}
<commit_msg>Improved search<commit_after>package main

import (
	"fmt"
	"strings"

	"github.com/aerogo/flow"
	"github.com/animenotifier/arn"
	"github.com/fatih/color"
)

func main() {
	color.Yellow("Updating search index")

	flow.Parallel(updateAnimeIndex, updateUserIndex)

	color.Green("Finished.")
}

func updateAnimeIndex() {
	animeSearchIndex := arn.NewSearchIndex()

	// Anime
	animeStream, err := arn.StreamAnime()

	if err != nil {
		panic(err)
	}

	for anime := range animeStream {
		if anime.Title.Canonical != "" {
			animeSearchIndex.TextToID[strings.ToLower(anime.Title.Canonical)] = anime.ID
		}

		if anime.Title.Romaji != "" {
			animeSearchIndex.TextToID[strings.ToLower(anime.Title.Romaji)] = anime.ID
		}

		// Make sure we only include Japanese titles that
		// don't overlap with the English titles.
		if anime.Title.Japanese != "" && animeSearchIndex.TextToID[strings.ToLower(anime.Title.Japanese)] == "" {
			animeSearchIndex.TextToID[strings.ToLower(anime.Title.Japanese)] = anime.ID
		}

		// Same with English titles, don't overwrite other stuff.
		if anime.Title.English != "" && animeSearchIndex.TextToID[strings.ToLower(anime.Title.English)] == "" {
			animeSearchIndex.TextToID[strings.ToLower(anime.Title.English)] = anime.ID
		}

		for _, synonym := range anime.Title.Synonyms {
			synonym = strings.ToLower(synonym)

			if synonym != "" && len(synonym) <= 10 {
				animeSearchIndex.TextToID[synonym] = anime.ID
			}
		}
	}

	fmt.Println(len(animeSearchIndex.TextToID), "anime titles")

	// Save in database
	err = arn.DB.Set("SearchIndex", "Anime", animeSearchIndex)

	if err != nil {
		panic(err)
	}
}

func updateUserIndex() {
	userSearchIndex := arn.NewSearchIndex()

	// Users
	userStream, err := arn.StreamUsers()

	if err != nil {
		panic(err)
	}

	for user := range userStream {
		if user.IsActive() && user.Nick != "" {
			userSearchIndex.TextToID[strings.ToLower(user.Nick)] = user.ID
		}
	}

	fmt.Println(len(userSearchIndex.TextToID), "user names")

	// Save in database
	err = arn.DB.Set("SearchIndex", "User", userSearchIndex)

	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package routing

import (
	"net/http"
	"testing"
)

func TestAddRouteToRouter(t *testing.T) {

	r := Router{}
	r.AddRoute(Route{
		Name:    "TestRoute",
		Method:  "GET",
		Pattern: "/test",
		HandlerFunc: func(w http.ResponseWriter, r *http.Request) {

		}})

	if r.Routes[0].Name != "TestRoute" {
		t.Error("Route was not added.")
	}

}
<commit_msg>Added test to make sure mux parses routes<commit_after>package routing

import (
	"fmt"
	"github.com/gorilla/mux"
	"net/http"
	"testing"
)

func TestAddRouteToRouter(t *testing.T) {

	r := Router{}
	r.AddRoute(Route{
		Name:    "TestRoute",
		Method:  "GET",
		Pattern: "/test",
		HandlerFunc: func(w http.ResponseWriter, r *http.Request) {

		}})

	if r.Routes[0].Name != "TestRoute" {
		t.Error("Route was not added.")
	}

	g := r.ToMux()

	g.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
		tr, err := route.GetPathTemplate()
		if err != nil {
			return err
		}
		if tr != "/test" {
			t.Error(fmt.Sprintf("Route failed: %s", tr))
		}
		return nil
	})

}
<|endoftext|>


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

package container

import (
	"math/rand"
	"sync"
	"testing"
	"time"

	"github.com/google/cadvisor/info"
)

type mockContainer struct {
}

func (self *mockContainer) GetSpec() (*info.ContainerSpec, error) {
	return nil, nil
}
func (self *mockContainer) ListContainers(listType ListType) ([]info.ContainerReference, error) {
	return nil, nil
}

func (self *mockContainer) ListThreads(listType ListType) ([]int, error) {
	return nil, nil
}

func (self *mockContainer) ListProcesses(listType ListType) ([]int, error) {
	return nil, nil
}

func TestMaxMemoryUsage(t *testing.T) {
	N := 100
	memTrace := make([]uint64, N)
	for i := 0; i < N; i++ {
		memTrace[i] = uint64(i + 1)
	}
	handler, err := AddStatsSummary(
		containerWithTrace(1*time.Second, nil, memTrace),
		&StatsParameter{
			Sampler:    "uniform",
			NumSamples: 10,
		},
	)
	if err != nil {
		t.Error(err)
	}
	maxUsage := uint64(N)
	for i := 0; i < N; i++ {
		_, err := handler.GetStats()
		if err != nil {
			t.Errorf("Error when get stats: %v", err)
			continue
		}
	}
	summary, err := handler.StatsPercentiles()
	if err != nil {
		t.Fatalf("Error when get summary: %v", err)
	}
	if summary.MaxMemoryUsage != maxUsage {
		t.Fatalf("Max memory usage should be %v; received %v", maxUsage, summary.MaxMemoryUsage)
	}
}

type replayTrace struct {
	NoStatsSummary
	mockContainer
	cpuTrace    []uint64
	memTrace    []uint64
	totalUsage  uint64
	currenttime time.Time
	duration    time.Duration
	lock        sync.Mutex
}

func containerWithTrace(duration time.Duration, cpuUsages []uint64, memUsages []uint64) ContainerHandler {
	return &replayTrace{
		duration:    duration,
		cpuTrace:    cpuUsages,
		memTrace:    memUsages,
		currenttime: time.Now(),
	}
}

func (self *replayTrace) ContainerReference() (info.ContainerReference, error) {
	return info.ContainerReference{
		Name: "replay",
	}, nil
}

func (self *replayTrace) GetStats() (*info.ContainerStats, error) {
	stats := new(info.ContainerStats)
	stats.Cpu = new(info.CpuStats)
	stats.Memory = new(info.MemoryStats)
	if len(self.memTrace) > 0 {
		stats.Memory.Usage = self.memTrace[0]
		self.memTrace = self.memTrace[1:]
	}

	self.lock.Lock()
	defer self.lock.Unlock()

	cpuTrace := self.totalUsage
	if len(self.cpuTrace) > 0 {
		cpuTrace += self.cpuTrace[0]
		self.cpuTrace = self.cpuTrace[1:]
	}
	self.totalUsage = cpuTrace
	stats.Timestamp = self.currenttime
	self.currenttime = self.currenttime.Add(self.duration)
	stats.Cpu.Usage.Total = cpuTrace
	stats.Cpu.Usage.PerCpu = []uint64{cpuTrace}
	stats.Cpu.Usage.User = cpuTrace
	stats.Cpu.Usage.System = 0
	return stats, nil
}

func TestSampleCpuUsage(t *testing.T) {
	// Number of samples
	N := 10
	cpuTrace := make([]uint64, 0, N)
	memTrace := make([]uint64, 0, N)

	// We need N+1 observations to get N samples
	for i := 0; i < N+1; i++ {
		cpuusage := uint64(rand.Intn(1000))
		memusage := uint64(rand.Intn(1000))
		cpuTrace = append(cpuTrace, cpuusage)
		memTrace = append(memTrace, memusage)
	}

	samplePeriod := 1 * time.Second

	handler, err := AddStatsSummary(
		containerWithTrace(samplePeriod, cpuTrace, memTrace),
		&StatsParameter{
			// Use uniform sampler with sample size of N, so that
			// we will be guaranteed to store the first N samples.
			Sampler:    "uniform",
			NumSamples: N,
		},
	)
	if err != nil {
		t.Error(err)
	}

	// request stats/observation N+1 times, so that there will be N samples
	for i := 0; i < N+1; i++ {
		_, err = handler.GetStats()
		if err != nil {
			t.Fatal(err)
		}
	}

	_, err := handler.StatsPercentiles()
	if err != nil {
		t.Fatal(err)
	}
	hs := handler.(*percentilesContainerHandlerWrapper)
	hs.sampler.Map(func(d interface{}) {
		sample := d.(*info.ContainerStatsSample)
		if sample.Duration != samplePeriod {
			t.Errorf("sample duration is %v, not %v", sample.Duration, samplePeriod)
		}
		cpuUsage := sample.Cpu.Usage
		found := false
		for _, u := range cpuTrace {
			if u == cpuUsage {
				found = true
			}
		}
		if !found {
			t.Errorf("unable to find cpu usage %v", cpuUsage)
		}
	})
}
<commit_msg>unit test<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 container

import (
	"math/rand"
	"sync"
	"testing"
	"time"

	"github.com/google/cadvisor/info"
)

type mockContainer struct {
}

func (self *mockContainer) GetSpec() (*info.ContainerSpec, error) {
	return nil, nil
}
func (self *mockContainer) ListContainers(listType ListType) ([]info.ContainerReference, error) {
	return nil, nil
}

func (self *mockContainer) ListThreads(listType ListType) ([]int, error) {
	return nil, nil
}

func (self *mockContainer) ListProcesses(listType ListType) ([]int, error) {
	return nil, nil
}

func TestMaxMemoryUsage(t *testing.T) {
	N := 100
	memTrace := make([]uint64, N)
	for i := 0; i < N; i++ {
		memTrace[i] = uint64(i + 1)
	}
	handler, err := AddStatsSummary(
		containerWithTrace(1*time.Second, nil, memTrace),
		&StatsParameter{
			Sampler:    "uniform",
			NumSamples: 10,
		},
	)
	if err != nil {
		t.Error(err)
	}
	maxUsage := uint64(N)
	for i := 0; i < N; i++ {
		_, err := handler.GetStats()
		if err != nil {
			t.Errorf("Error when get stats: %v", err)
			continue
		}
	}
	summary, err := handler.StatsPercentiles()
	if err != nil {
		t.Fatalf("Error when get summary: %v", err)
	}
	if summary.MaxMemoryUsage != maxUsage {
		t.Fatalf("Max memory usage should be %v; received %v", maxUsage, summary.MaxMemoryUsage)
	}
}

type replayTrace struct {
	NoStatsSummary
	mockContainer
	cpuTrace    []uint64
	memTrace    []uint64
	totalUsage  uint64
	currenttime time.Time
	duration    time.Duration
	lock        sync.Mutex
}

func containerWithTrace(duration time.Duration, cpuUsages []uint64, memUsages []uint64) ContainerHandler {
	return &replayTrace{
		duration:    duration,
		cpuTrace:    cpuUsages,
		memTrace:    memUsages,
		currenttime: time.Now(),
	}
}

func (self *replayTrace) ContainerReference() (info.ContainerReference, error) {
	return info.ContainerReference{
		Name: "replay",
	}, nil
}

func (self *replayTrace) GetStats() (*info.ContainerStats, error) {
	stats := new(info.ContainerStats)
	stats.Cpu = new(info.CpuStats)
	stats.Memory = new(info.MemoryStats)
	if len(self.memTrace) > 0 {
		stats.Memory.Usage = self.memTrace[0]
		self.memTrace = self.memTrace[1:]
	}

	self.lock.Lock()
	defer self.lock.Unlock()

	cpuTrace := self.totalUsage
	if len(self.cpuTrace) > 0 {
		cpuTrace += self.cpuTrace[0]
		self.cpuTrace = self.cpuTrace[1:]
	}
	self.totalUsage = cpuTrace
	stats.Timestamp = self.currenttime
	self.currenttime = self.currenttime.Add(self.duration)
	stats.Cpu.Usage.Total = cpuTrace
	stats.Cpu.Usage.PerCpu = []uint64{cpuTrace}
	stats.Cpu.Usage.User = cpuTrace
	stats.Cpu.Usage.System = 0
	return stats, nil
}

func TestSampleCpuUsage(t *testing.T) {
	// Number of samples
	N := 10
	cpuTrace := make([]uint64, 0, N)
	memTrace := make([]uint64, 0, N)

	// We need N+1 observations to get N samples
	for i := 0; i < N+1; i++ {
		cpuusage := uint64(rand.Intn(1000))
		memusage := uint64(rand.Intn(1000))
		cpuTrace = append(cpuTrace, cpuusage)
		memTrace = append(memTrace, memusage)
	}

	samplePeriod := 1 * time.Second

	handler, err := AddStatsSummary(
		containerWithTrace(samplePeriod, cpuTrace, memTrace),
		&StatsParameter{
			// Use uniform sampler with sample size of N, so that
			// we will be guaranteed to store the first N samples.
			Sampler:    "uniform",
			NumSamples: N,
		},
	)
	if err != nil {
		t.Error(err)
	}

	// request stats/observation N+1 times, so that there will be N samples
	for i := 0; i < N+1; i++ {
		_, err = handler.GetStats()
		if err != nil {
			t.Fatal(err)
		}
	}

	_, err = handler.StatsPercentiles()
	if err != nil {
		t.Fatal(err)
	}
	hs := handler.(*percentilesContainerHandlerWrapper)
	hs.sampler.Map(func(d interface{}) {
		sample := d.(*info.ContainerStatsSample)
		if sample.Duration != samplePeriod {
			t.Errorf("sample duration is %v, not %v", sample.Duration, samplePeriod)
		}
		cpuUsage := sample.Cpu.Usage
		found := false
		for _, u := range cpuTrace {
			if u == cpuUsage {
				found = true
			}
		}
		if !found {
			t.Errorf("unable to find cpu usage %v", cpuUsage)
		}
	})
}
<|endoftext|>


<commit_before>package content

import (
	"encoding/json"
	"errors"
	"time"

	cmneo4j "github.com/Financial-Times/cm-neo4j-driver"
)

const LiveBlogPackage = "LiveBlogPackage"
const LiveBlogPost = "LiveBlogPost"

var contentTypesWithNoBody = map[string]bool{
	"Content":        true,
	"Article":        true,
	"Video":          true,
	"Graphic":        true,
	"Audio":          true,
	"ContentPackage": true,
	LiveBlogPackage:  true,
	LiveBlogPost:     true,
}

type Service struct {
	driver *cmneo4j.Driver
}

//NewCypherDriver instantiate driver
func NewContentService(d *cmneo4j.Driver) Service {
	return Service{
		driver: d,
	}
}

//Initialise ensures constraints on content uuid
func (cd Service) Initialise() error {

	return cd.driver.EnsureConstraints(map[string]string{
		"Content": "uuid"})
}

// Check - Feeds into the Healthcheck and checks whether we can connect to Neo and that the datastore isn't empty and
// also checks if we are connected to leader/writable node
func (cd Service) Check() error {
	return cd.driver.VerifyConnectivity()
}

// Read - reads a content given a UUID
func (cd Service) Read(uuid string, transID string) (interface{}, bool, error) {
	var results []struct {
		content
	}

	query := &cmneo4j.Query{
		Cypher: `MATCH (n:Content {uuid:{uuid}})
			OPTIONAL MATCH (sp:Thing)-[rel1:IS_CURATED_FOR]->(n)
			OPTIONAL MATCH (n)-[rel2:CONTAINS]->(cp:Thing)
			WITH n,sp,cp
			RETURN n.uuid as uuid,
				n.title as title,
				n.publishedDate as publishedDate,
				sp.uuid as storyPackage,
				cp.uuid as contentPackage`,
		Params: map[string]interface{}{
			"uuid": uuid,
		},
		Result: &results,
	}

	err := cd.driver.Read(query)

	if errors.Is(err, cmneo4j.ErrNoResultsFound) {
		return content{}, false, err
	}

	if err != nil {
		return content{}, false, err
	}

	result := results[0]

	contentItem := content{
		UUID:           result.UUID,
		Title:          result.Title,
		PublishedDate:  result.PublishedDate,
		StoryPackage:   result.StoryPackage,
		ContentPackage: result.ContentPackage,
	}
	return contentItem, true, nil
}

//Write - Writes a content node
func (cd Service) Write(thing interface{}, transID string) error {
	c := thing.(content)

	// Letting through only articles (which have body), live blogs, content packages, graphics, videos and audios (which don't have a body)
	if c.Body == "" && !contentTypesWithNoBody[c.Type] {
		return nil
	}

	params := map[string]interface{}{
		"uuid": c.UUID,
	}

	if c.Title != "" {
		params["title"] = c.Title
		params["prefLabel"] = c.Title
	}

	if c.PublishedDate != "" {
		params["publishedDate"] = c.PublishedDate
		datetimeEpoch, err := time.Parse(time.RFC3339, c.PublishedDate)

		if err != nil {
			return err
		}

		params["publishedDateEpoch"] = datetimeEpoch.Unix()
	}

	deleteEntityRelationshipsQuery := &cmneo4j.Query{
		Cypher: `MATCH (t:Thing {uuid:{uuid}})
				OPTIONAL MATCH (c:Thing)-[rel1:IS_CURATED_FOR]->(t)
				OPTIONAL MATCH (cp:Thing)<-[rel2:CONTAINS]-(t)
				DELETE rel1, rel2`,
		Params: map[string]interface{}{
			"uuid": c.UUID,
		},
	}

	queries := []*cmneo4j.Query{deleteEntityRelationshipsQuery}

	labels := `:Content`

	if c.StoryPackage != "" {
		addStoryPackageRelationQuery := addStoryPackageRelationQuery(c.UUID, c.StoryPackage)
		queries = append(queries, addStoryPackageRelationQuery)
	}

	if c.ContentPackage != "" {
		addContentPackageRelationQuery := addContentPackageRelationQuery(c.UUID, c.ContentPackage)
		queries = append(queries, addContentPackageRelationQuery)

		labels = labels + `:ContentPackage`
		if c.Type == LiveBlogPackage {
			labels = labels + `:` + LiveBlogPackage
		}
	}

	query := `MERGE (n:Thing {uuid: {uuid}})
		      set n={allprops}
		      set n ` + labels

	writeContentQuery := &cmneo4j.Query{
		Cypher: query,
		Params: map[string]interface{}{
			"uuid":     c.UUID,
			"allprops": params,
		},
	}

	queries = append(queries, writeContentQuery)
	err := cd.driver.Write(queries...)
	if err != nil {
		return err
	}
	return nil
}

func addStoryPackageRelationQuery(articleUUID, packageUUID string) *cmneo4j.Query {
	query := `MERGE(sp:Thing{uuid:{packageUuid}})
			MERGE(c:Thing{uuid:{contentUuid}})
			MERGE(c)<-[rel:IS_CURATED_FOR]-(sp)`

	return &cmneo4j.Query{
		Cypher: query,
		Params: map[string]interface{}{
			"packageUuid": packageUUID,
			"contentUuid": articleUUID,
		},
	}
}

func addContentPackageRelationQuery(articleUUID, packageUUID string) *cmneo4j.Query {
	query := `MERGE(cp:Thing{uuid:{packageUuid}})
			MERGE(c:Thing{uuid:{contentUuid}})
			MERGE(c)-[rel:CONTAINS]->(cp)`

	return &cmneo4j.Query{
		Cypher: query,
		Params: map[string]interface{}{
			"packageUuid": packageUUID,
			"contentUuid": articleUUID,
		},
	}
}

//Delete - Deletes a content item
func (cd Service) Delete(uuid string, transID string) (bool, error) {
	// "clearCollectionNode" query handles a specific case when
	// a Content Collection was deleted, which means its contents are removed
	// and the "ContentCollection" label was removed, but the node remains in Neo4j
	// with the label "Thing" only and still has a relation to a Content Package.
	// When a delete request occurs for the very same Content Package,
	// the related hanging node gets deleted by this query.

	// Check "content-collection-rw-neo4j" service for the the Content Collection deletion query.
	clearCollectionNode := &cmneo4j.Query{
		Cypher: `
			MATCH (p:ContentPackage {uuid: {uuid}})-[rel:CONTAINS]->(cc:Thing)
			OPTIONAL MATCH (cc)-[rel]-()
			WITH cc, count(rel) AS relCount
			WHERE relCount = 1 AND NOT cc:ContentCollection
			DETACH DELETE cc
		`,
		Params: map[string]interface{}{
			"uuid": uuid,
		},
	}

	removeNode := &cmneo4j.Query{
		Cypher: `
			MATCH (p:Thing {uuid: {uuid}})
			DETACH DELETE p
		`,
		Params: map[string]interface{}{
			"uuid": uuid,
		},
		IncludeSummary: true,
	}

	err := cd.driver.Write(clearCollectionNode)
	if err != nil {
		return false, err
	}
	// The queries should be executed in the specified order but `CypherBatch` does not guarantee order,
	// so we execute them in separate batches
	// dependency: if a CP is deleted before the first query is executed, there is no way to find the related node
	// left after a ContentCollections is deleted
	err = cd.driver.Write(removeNode)
	if err != nil {
		return false, err
	}

	s1, err := removeNode.Summary()
	if err != nil {
		return false, err
	}
	return s1.Counters().NodesDeleted() > 0, nil
}

// DecodeJSON - Decodes JSON into content
func (cd Service) DecodeJSON(dec *json.Decoder) (interface{}, string, error) {
	c := content{}
	err := dec.Decode(&c)
	return c, c.UUID, err
}

// Count - Returns a count of the number of content items in this Neo instance
func (cd Service) Count() (int, error) {

	var results []struct {
		Count int `json:"c"`
	}

	query := &cmneo4j.Query{
		Cypher: `MATCH (n:Content) return count(n) as c`,
		Result: &results,
	}

	err := cd.driver.Read(query)

	if errors.Is(err, cmneo4j.ErrNoResultsFound) {
		return 0, nil
	}

	if err != nil {
		return 0, err
	}

	return results[0].Count, nil
}
<commit_msg>Add ignore exception in Initialise()<commit_after>package content

import (
	"encoding/json"
	"errors"
	"time"

	cmneo4j "github.com/Financial-Times/cm-neo4j-driver"
)

const LiveBlogPackage = "LiveBlogPackage"
const LiveBlogPost = "LiveBlogPost"

var contentTypesWithNoBody = map[string]bool{
	"Content":        true,
	"Article":        true,
	"Video":          true,
	"Graphic":        true,
	"Audio":          true,
	"ContentPackage": true,
	LiveBlogPackage:  true,
	LiveBlogPost:     true,
}

type Service struct {
	driver *cmneo4j.Driver
}

//NewCypherDriver instantiate driver
func NewContentService(d *cmneo4j.Driver) Service {
	return Service{
		driver: d,
	}
}

//Initialise ensures constraints on content uuid
func (cd Service) Initialise() error {
	err := cd.driver.EnsureConstraints(map[string]string{
		"Content": "uuid"})

	// We are ignoring ErrNeo4jVersionNotSupported because the service is expected
	// to work with Neo4j v4 and if it's working Neo4j v3.x we expect that
	// the required constraints are already created in Neo4j.
	if errors.Is(err, cmneo4j.ErrNeo4jVersionNotSupported) {
		return nil
	}
	return err
}

// Check - Feeds into the Healthcheck and checks whether we can connect to Neo and that the datastore isn't empty and
// also checks if we are connected to leader/writable node
func (cd Service) Check() error {
	return cd.driver.VerifyConnectivity()
}

// Read - reads a content given a UUID
func (cd Service) Read(uuid string, transID string) (interface{}, bool, error) {
	var results []struct {
		content
	}

	query := &cmneo4j.Query{
		Cypher: `MATCH (n:Content {uuid:{uuid}})
			OPTIONAL MATCH (sp:Thing)-[rel1:IS_CURATED_FOR]->(n)
			OPTIONAL MATCH (n)-[rel2:CONTAINS]->(cp:Thing)
			WITH n,sp,cp
			RETURN n.uuid as uuid,
				n.title as title,
				n.publishedDate as publishedDate,
				sp.uuid as storyPackage,
				cp.uuid as contentPackage`,
		Params: map[string]interface{}{
			"uuid": uuid,
		},
		Result: &results,
	}

	err := cd.driver.Read(query)

	if errors.Is(err, cmneo4j.ErrNoResultsFound) {
		return content{}, false, err
	}

	if err != nil {
		return content{}, false, err
	}

	result := results[0]

	contentItem := content{
		UUID:           result.UUID,
		Title:          result.Title,
		PublishedDate:  result.PublishedDate,
		StoryPackage:   result.StoryPackage,
		ContentPackage: result.ContentPackage,
	}
	return contentItem, true, nil
}

//Write - Writes a content node
func (cd Service) Write(thing interface{}, transID string) error {
	c := thing.(content)

	// Letting through only articles (which have body), live blogs, content packages, graphics, videos and audios (which don't have a body)
	if c.Body == "" && !contentTypesWithNoBody[c.Type] {
		return nil
	}

	params := map[string]interface{}{
		"uuid": c.UUID,
	}

	if c.Title != "" {
		params["title"] = c.Title
		params["prefLabel"] = c.Title
	}

	if c.PublishedDate != "" {
		params["publishedDate"] = c.PublishedDate
		datetimeEpoch, err := time.Parse(time.RFC3339, c.PublishedDate)

		if err != nil {
			return err
		}

		params["publishedDateEpoch"] = datetimeEpoch.Unix()
	}

	deleteEntityRelationshipsQuery := &cmneo4j.Query{
		Cypher: `MATCH (t:Thing {uuid:{uuid}})
				OPTIONAL MATCH (c:Thing)-[rel1:IS_CURATED_FOR]->(t)
				OPTIONAL MATCH (cp:Thing)<-[rel2:CONTAINS]-(t)
				DELETE rel1, rel2`,
		Params: map[string]interface{}{
			"uuid": c.UUID,
		},
	}

	queries := []*cmneo4j.Query{deleteEntityRelationshipsQuery}

	labels := `:Content`

	if c.StoryPackage != "" {
		addStoryPackageRelationQuery := addStoryPackageRelationQuery(c.UUID, c.StoryPackage)
		queries = append(queries, addStoryPackageRelationQuery)
	}

	if c.ContentPackage != "" {
		addContentPackageRelationQuery := addContentPackageRelationQuery(c.UUID, c.ContentPackage)
		queries = append(queries, addContentPackageRelationQuery)

		labels = labels + `:ContentPackage`
		if c.Type == LiveBlogPackage {
			labels = labels + `:` + LiveBlogPackage
		}
	}

	query := `MERGE (n:Thing {uuid: {uuid}})
		      set n={allprops}
		      set n ` + labels

	writeContentQuery := &cmneo4j.Query{
		Cypher: query,
		Params: map[string]interface{}{
			"uuid":     c.UUID,
			"allprops": params,
		},
	}

	queries = append(queries, writeContentQuery)
	err := cd.driver.Write(queries...)
	if err != nil {
		return err
	}
	return nil
}

func addStoryPackageRelationQuery(articleUUID, packageUUID string) *cmneo4j.Query {
	query := `MERGE(sp:Thing{uuid:{packageUuid}})
			MERGE(c:Thing{uuid:{contentUuid}})
			MERGE(c)<-[rel:IS_CURATED_FOR]-(sp)`

	return &cmneo4j.Query{
		Cypher: query,
		Params: map[string]interface{}{
			"packageUuid": packageUUID,
			"contentUuid": articleUUID,
		},
	}
}

func addContentPackageRelationQuery(articleUUID, packageUUID string) *cmneo4j.Query {
	query := `MERGE(cp:Thing{uuid:{packageUuid}})
			MERGE(c:Thing{uuid:{contentUuid}})
			MERGE(c)-[rel:CONTAINS]->(cp)`

	return &cmneo4j.Query{
		Cypher: query,
		Params: map[string]interface{}{
			"packageUuid": packageUUID,
			"contentUuid": articleUUID,
		},
	}
}

//Delete - Deletes a content item
func (cd Service) Delete(uuid string, transID string) (bool, error) {
	// "clearCollectionNode" query handles a specific case when
	// a Content Collection was deleted, which means its contents are removed
	// and the "ContentCollection" label was removed, but the node remains in Neo4j
	// with the label "Thing" only and still has a relation to a Content Package.
	// When a delete request occurs for the very same Content Package,
	// the related hanging node gets deleted by this query.

	// Check "content-collection-rw-neo4j" service for the the Content Collection deletion query.
	clearCollectionNode := &cmneo4j.Query{
		Cypher: `
			MATCH (p:ContentPackage {uuid: {uuid}})-[rel:CONTAINS]->(cc:Thing)
			OPTIONAL MATCH (cc)-[rel]-()
			WITH cc, count(rel) AS relCount
			WHERE relCount = 1 AND NOT cc:ContentCollection
			DETACH DELETE cc
		`,
		Params: map[string]interface{}{
			"uuid": uuid,
		},
	}

	removeNode := &cmneo4j.Query{
		Cypher: `
			MATCH (p:Thing {uuid: {uuid}})
			DETACH DELETE p
		`,
		Params: map[string]interface{}{
			"uuid": uuid,
		},
		IncludeSummary: true,
	}

	err := cd.driver.Write(clearCollectionNode)
	if err != nil {
		return false, err
	}
	// The queries should be executed in the specified order but `CypherBatch` does not guarantee order,
	// so we execute them in separate batches
	// dependency: if a CP is deleted before the first query is executed, there is no way to find the related node
	// left after a ContentCollections is deleted
	err = cd.driver.Write(removeNode)
	if err != nil {
		return false, err
	}

	s1, err := removeNode.Summary()
	if err != nil {
		return false, err
	}
	return s1.Counters().NodesDeleted() > 0, nil
}

// DecodeJSON - Decodes JSON into content
func (cd Service) DecodeJSON(dec *json.Decoder) (interface{}, string, error) {
	c := content{}
	err := dec.Decode(&c)
	return c, c.UUID, err
}

// Count - Returns a count of the number of content items in this Neo instance
func (cd Service) Count() (int, error) {

	var results []struct {
		Count int `json:"c"`
	}

	query := &cmneo4j.Query{
		Cypher: `MATCH (n:Content) return count(n) as c`,
		Result: &results,
	}

	err := cd.driver.Read(query)

	if errors.Is(err, cmneo4j.ErrNoResultsFound) {
		return 0, nil
	}

	if err != nil {
		return 0, err
	}

	return results[0].Count, nil
}
<|endoftext|>


<commit_before>package uploader

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"time"

	uuid "github.com/satori/go.uuid"
	"github.com/spf13/viper"
)

// Config s3 uploader config data type
type Config struct {
	ID             string `jsonapi:"primary,uploader_config"`
	Key            string `jsonapi:"attr,key"`
	ACL            string `jsonapi:"attr,acl"`
	AwsAccessKeyID string `jsonapi:"attr,awsAccessKeyId"`
	S3Policy       string `jsonapi:"attr,s3Policy"`
	S3Signature    string `jsonapi:"attr,s3Signature"`
	S3Bucket       string `jsonapi:"attr,s3Bucket"`
	// ContentType    string `jsonapi:"attr,contentType"`
}

// Policy data type
type Policy struct {
	Expiration string        `json:"expiration"`
	Conditions []interface{} `json:"conditions"`
}

// ACL ...
func ACL() string {
	return "public-read"
}

// PolicyString ...
func PolicyString(key string) string {
	policyData := GetPolicy(key)
	policyDataJSON, _ := json.Marshal(policyData)
	return base64.StdEncoding.EncodeToString(policyDataJSON)
}

// Signature policy signature
func Signature(policy string) string {
	sigKey := []byte(viper.GetString("aws_secret"))

	sig := hmac.New(sha1.New, sigKey)
	sig.Write([]byte(policy))
	return base64.StdEncoding.EncodeToString(sig.Sum(nil))
}

// Key s3 file key
func Key(pref string) string {
	key, err := uuid.NewV4()
	if err != nil {
		panic(err)
	}
	return fmt.Sprintf("%s/%s/", pref, key)
}

// GetPolicy generate policy for the file
func GetPolicy(key string) Policy {
	data := Policy{
		time.Now().UTC().Add(time.Hour * 10).Format(time.RFC3339),
		[]interface{}{
			[]string{"starts-with", "$key", key},
			map[string]string{"bucket": viper.GetString("aws_bucket_name")},
			map[string]string{"acl": ACL()},
			map[string]string{"success_action_status": "201"},
			[]interface{}{"content-length-range", 1, 104857600}, // TODO: use max size based on the type of the resource
		},
	}
	// TODO: validate content type based on the resource
	// conditions << [ 'starts-with', '$Content-Type', contentType]

	return data
}
<commit_msg>Update default s3 uploader max content size<commit_after>package uploader

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"time"

	uuid "github.com/satori/go.uuid"
	"github.com/spf13/viper"
)

// Config s3 uploader config data type
type Config struct {
	ID             string `jsonapi:"primary,uploader_config"`
	Key            string `jsonapi:"attr,key"`
	ACL            string `jsonapi:"attr,acl"`
	AwsAccessKeyID string `jsonapi:"attr,awsAccessKeyId"`
	S3Policy       string `jsonapi:"attr,s3Policy"`
	S3Signature    string `jsonapi:"attr,s3Signature"`
	S3Bucket       string `jsonapi:"attr,s3Bucket"`
	// ContentType    string `jsonapi:"attr,contentType"`
}

// Policy data type
type Policy struct {
	Expiration string        `json:"expiration"`
	Conditions []interface{} `json:"conditions"`
}

// ACL ...
func ACL() string {
	return "public-read"
}

// PolicyString ...
func PolicyString(key string) string {
	policyData := GetPolicy(key)
	policyDataJSON, _ := json.Marshal(policyData)
	return base64.StdEncoding.EncodeToString(policyDataJSON)
}

// Signature policy signature
func Signature(policy string) string {
	sigKey := []byte(viper.GetString("aws_secret"))

	sig := hmac.New(sha1.New, sigKey)
	sig.Write([]byte(policy))
	return base64.StdEncoding.EncodeToString(sig.Sum(nil))
}

// Key s3 file key
func Key(pref string) string {
	key, err := uuid.NewV4()
	if err != nil {
		panic(err)
	}
	return fmt.Sprintf("%s/%s/", pref, key)
}

// GetPolicy generate policy for the file
func GetPolicy(key string) Policy {
	data := Policy{
		time.Now().UTC().Add(time.Hour * 10).Format(time.RFC3339),
		[]interface{}{
			[]string{"starts-with", "$key", key},
			map[string]string{"bucket": viper.GetString("aws_bucket_name")},
			map[string]string{"acl": ACL()},
			map[string]string{"success_action_status": "201"},
			[]interface{}{"content-length-range", 1, 1073741824}, // TODO: use max size based on the type of the resource
		},
	}
	// TODO: validate content type based on the resource
	// conditions << [ 'starts-with', '$Content-Type', contentType]

	return data
}
<|endoftext|>


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

package vgo

import (
	"strings"

	"cmd/go/internal/base"
	"cmd/go/internal/modfetch"
	"cmd/go/internal/module"
	"cmd/go/internal/mvs"
	"cmd/go/internal/semver"
)

var CmdGet = &base.Command{
	UsageLine: "get [build flags] [packages]",
	Short:     "download and install versioned modules and dependencies",
	Long: `
Get downloads the latest versions of modules containing the named packages,
along with the versions of the dependencies required by those modules
(not necessarily the latest ones).

It then installs the named packages, like 'go install'.

The -u flag causes get to download the latest version of dependencies as well.

Each package being updated can be suffixed with @version to specify
the desired version. Specifying a version older than the one currently
in use causes a downgrade, which may in turn downgrade other
modules using that one, to keep everything consistent.

TODO: Make this documentation better once the semantic dust settles.
	`,
}

var getU = CmdGet.Flag.Bool("u", false, "")

func init() {
	CmdGet.Run = runGet // break init loop
}

func runGet(cmd *base.Command, args []string) {
	if *getU && len(args) > 0 {
		base.Fatalf("vgo get: -u not supported with argument list")
	}
	if !*getU && len(args) == 0 {
		base.Fatalf("vgo get: need arguments or -u")
	}

	if *getU {
		isGetU = true
		ImportPaths([]string{"."})
		return
	}

	Init()
	InitMod()
	var upgrade []module.Version
	var downgrade []module.Version
	var newPkgs []string
	for _, pkg := range args {
		var path, vers string
		/* OLD CODE
		if n := strings.Count(pkg, "(") + strings.Count(pkg, ")"); n > 0 {
			i := strings.Index(pkg, "(")
			j := strings.Index(pkg, ")")
			if n != 2 || i < 0 || j <= i+1 || j != len(pkg)-1 && pkg[j+1] != '/' {
				base.Errorf("vgo get: invalid module version syntax: %s", pkg)
				continue
			}
			path, vers = pkg[:i], pkg[i+1:j]
			pkg = pkg[:i] + pkg[j+1:]
		*/
		if i := strings.Index(pkg, "@"); i >= 0 {
			path, pkg, vers = pkg[:i], pkg[:i], pkg[i+1:]
			if strings.Contains(vers, "@") {
				base.Errorf("vgo get: invalid module version syntax: %s", pkg)
				continue
			}
		} else {
			path = pkg
			vers = "latest"
		}
		if vers == "none" {
			downgrade = append(downgrade, module.Version{path, ""})
		} else {
			info, err := modfetch.Query(path, vers, allowed)
			if err != nil {
				base.Errorf("vgo get %v: %v", pkg, err)
				continue
			}
			upgrade = append(upgrade, module.Version{Path: path, Version: info.Version})
			newPkgs = append(newPkgs, pkg)
		}
	}
	args = newPkgs

	// Upgrade.
	var err error
	buildList, err = mvs.Upgrade(Target, newReqs(), upgrade...)
	if err != nil {
		base.Fatalf("vgo get: %v", err)
	}

	importPaths([]string{"."})

	// Downgrade anything that went too far.
	version := make(map[string]string)
	for _, mod := range buildList {
		version[mod.Path] = mod.Version
	}
	for _, mod := range upgrade {
		if semver.Compare(mod.Version, version[mod.Path]) < 0 {
			downgrade = append(downgrade, mod)
		}
	}

	if len(downgrade) > 0 {
		buildList, err = mvs.Downgrade(Target, newReqs(buildList[1:]...), downgrade...)
		if err != nil {
			base.Fatalf("vgo get: %v", err)
		}

		// TODO: Check that everything we need to import is still available.
		/*
			local := v.matchPackages("all", v.Reqs[:1])
			for _, path := range local {
				dir, err := v.importDir(path)
				if err != nil {
					return err // TODO
				}
				imports, testImports, err := imports.ScanDir(dir, v.Tags)
				for _, path := range imports {
					xxx
				}
				for _, path := range testImports {
					xxx
				}
			}
		*/
	}
	writeGoMod()

	if len(args) > 0 {
		InstallHook(args)
	}
}

// Call into "go install". Set by internal/work, which imports us.
var InstallHook func([]string)
<commit_msg>vendor/cmd/go/internal/vgo: disable password prompt and SSH connection pooling<commit_after>// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package vgo

import (
	"os"
	"strings"

	"cmd/go/internal/base"
	"cmd/go/internal/modfetch"
	"cmd/go/internal/module"
	"cmd/go/internal/mvs"
	"cmd/go/internal/semver"
)

var CmdGet = &base.Command{
	UsageLine: "get [build flags] [packages]",
	Short:     "download and install versioned modules and dependencies",
	Long: `
Get downloads the latest versions of modules containing the named packages,
along with the versions of the dependencies required by those modules
(not necessarily the latest ones).

It then installs the named packages, like 'go install'.

The -u flag causes get to download the latest version of dependencies as well.

Each package being updated can be suffixed with @version to specify
the desired version. Specifying a version older than the one currently
in use causes a downgrade, which may in turn downgrade other
modules using that one, to keep everything consistent.

TODO: Make this documentation better once the semantic dust settles.
	`,
}

var getU = CmdGet.Flag.Bool("u", false, "")

func init() {
	CmdGet.Run = runGet // break init loop
}

func runGet(cmd *base.Command, args []string) {
	if *getU && len(args) > 0 {
		base.Fatalf("vgo get: -u not supported with argument list")
	}
	if !*getU && len(args) == 0 {
		base.Fatalf("vgo get: need arguments or -u")
	}

	// Disable any prompting for passwords by Git.
	// Only has an effect for 2.3.0 or later, but avoiding
	// the prompt in earlier versions is just too hard.
	// If user has explicitly set GIT_TERMINAL_PROMPT=1, keep
	// prompting.
	// See golang.org/issue/9341 and golang.org/issue/12706.
	if os.Getenv("GIT_TERMINAL_PROMPT") == "" {
		os.Setenv("GIT_TERMINAL_PROMPT", "0")
	}

	// Disable any ssh connection pooling by Git.
	// If a Git subprocess forks a child into the background to cache a new connection,
	// that child keeps stdout/stderr open. After the Git subprocess exits,
	// os /exec expects to be able to read from the stdout/stderr pipe
	// until EOF to get all the data that the Git subprocess wrote before exiting.
	// The EOF doesn't come until the child exits too, because the child
	// is holding the write end of the pipe.
	// This is unfortunate, but it has come up at least twice
	// (see golang.org/issue/13453 and golang.org/issue/16104)
	// and confuses users when it does.
	// If the user has explicitly set GIT_SSH or GIT_SSH_COMMAND,
	// assume they know what they are doing and don't step on it.
	// But default to turning off ControlMaster.
	if os.Getenv("GIT_SSH") == "" && os.Getenv("GIT_SSH_COMMAND") == "" {
		os.Setenv("GIT_SSH_COMMAND", "ssh -o ControlMaster=no")
	}

	if *getU {
		isGetU = true
		ImportPaths([]string{"."})
		return
	}

	Init()
	InitMod()
	var upgrade []module.Version
	var downgrade []module.Version
	var newPkgs []string
	for _, pkg := range args {
		var path, vers string
		/* OLD CODE
		if n := strings.Count(pkg, "(") + strings.Count(pkg, ")"); n > 0 {
			i := strings.Index(pkg, "(")
			j := strings.Index(pkg, ")")
			if n != 2 || i < 0 || j <= i+1 || j != len(pkg)-1 && pkg[j+1] != '/' {
				base.Errorf("vgo get: invalid module version syntax: %s", pkg)
				continue
			}
			path, vers = pkg[:i], pkg[i+1:j]
			pkg = pkg[:i] + pkg[j+1:]
		*/
		if i := strings.Index(pkg, "@"); i >= 0 {
			path, pkg, vers = pkg[:i], pkg[:i], pkg[i+1:]
			if strings.Contains(vers, "@") {
				base.Errorf("vgo get: invalid module version syntax: %s", pkg)
				continue
			}
		} else {
			path = pkg
			vers = "latest"
		}
		if vers == "none" {
			downgrade = append(downgrade, module.Version{path, ""})
		} else {
			info, err := modfetch.Query(path, vers, allowed)
			if err != nil {
				base.Errorf("vgo get %v: %v", pkg, err)
				continue
			}
			upgrade = append(upgrade, module.Version{Path: path, Version: info.Version})
			newPkgs = append(newPkgs, pkg)
		}
	}
	args = newPkgs

	// Upgrade.
	var err error
	buildList, err = mvs.Upgrade(Target, newReqs(), upgrade...)
	if err != nil {
		base.Fatalf("vgo get: %v", err)
	}

	importPaths([]string{"."})

	// Downgrade anything that went too far.
	version := make(map[string]string)
	for _, mod := range buildList {
		version[mod.Path] = mod.Version
	}
	for _, mod := range upgrade {
		if semver.Compare(mod.Version, version[mod.Path]) < 0 {
			downgrade = append(downgrade, mod)
		}
	}

	if len(downgrade) > 0 {
		buildList, err = mvs.Downgrade(Target, newReqs(buildList[1:]...), downgrade...)
		if err != nil {
			base.Fatalf("vgo get: %v", err)
		}

		// TODO: Check that everything we need to import is still available.
		/*
			local := v.matchPackages("all", v.Reqs[:1])
			for _, path := range local {
				dir, err := v.importDir(path)
				if err != nil {
					return err // TODO
				}
				imports, testImports, err := imports.ScanDir(dir, v.Tags)
				for _, path := range imports {
					xxx
				}
				for _, path := range testImports {
					xxx
				}
			}
		*/
	}
	writeGoMod()

	if len(args) > 0 {
		InstallHook(args)
	}
}

// Call into "go install". Set by internal/work, which imports us.
var InstallHook func([]string)
<|endoftext|>


<commit_before>package edict2

import (
	"bufio"
	"encoding/json"
	"errors"
	"io"
)

type EDict struct {
	*bufio.Scanner
	TokenType int
	entry     *Entry
}

type Gloss struct {
	Common  bool
	English string
	Field   *string
	Related []string
	Tags    []string
}

type Entry struct {
	Common    bool
	Dialects  []string
	EntSeq    string `json:"ent_seq"`
	Fields    []string
	Furigana  string
	Glosses   []Gloss
	HasAudio  bool
	Japanese  string
	KanaTags  []string `json:"kana_tags"`
	KanjiTags []string `json:"kanji_tags"`
	Pos       []string
	Tags      []string
}

func New(r io.Reader) *EDict {
	s := bufio.NewScanner(r)
	edict := &EDict{
		Scanner: s,
	}
	return edict
}

var NoMoreEntries error = errors.New("No more entries to read")

func (edict *EDict) NextEntry() error {
	e, err := parseEntry(edict.Bytes())
	if err != nil {
		return err
	}
	edict.entry = e

	return nil
}

func (e *EDict) Entry() *Entry {
	return e.entry
}

func parseEntry(entry []byte) (*Entry, error) {
	var e Entry
	json.Unmarshal(entry, &e)

	return &e, nil
}
<commit_msg>remove unused error<commit_after>package edict2

import (
	"bufio"
	"encoding/json"
	"io"
)

type EDict struct {
	*bufio.Scanner
	TokenType int
	entry     *Entry
}

type Gloss struct {
	Common  bool
	English string
	Field   *string
	Related []string
	Tags    []string
}

type Entry struct {
	Common    bool
	Dialects  []string
	EntSeq    string `json:"ent_seq"`
	Fields    []string
	Furigana  string
	Glosses   []Gloss
	HasAudio  bool
	Japanese  string
	KanaTags  []string `json:"kana_tags"`
	KanjiTags []string `json:"kanji_tags"`
	Pos       []string
	Tags      []string
}

func New(r io.Reader) *EDict {
	s := bufio.NewScanner(r)
	edict := &EDict{
		Scanner: s,
	}
	return edict
}

func (edict *EDict) NextEntry() error {
	e, err := parseEntry(edict.Bytes())
	if err != nil {
		return err
	}
	edict.entry = e

	return nil
}

func (e *EDict) Entry() *Entry {
	return e.entry
}

func parseEntry(entry []byte) (*Entry, error) {
	var e Entry
	json.Unmarshal(entry, &e)

	return &e, nil
}
<|endoftext|>


<commit_before>package edit

import (
	"errors"
	"fmt"

	"github.com/elves/elvish/store"
	"github.com/elves/elvish/util"
)

// Command history listing mode.

var ErrStoreOffline = errors.New("store offline")

type histlist struct {
	all      []string
	filtered []string
}

func (hl *histlist) Len() int {
	return len(hl.filtered)
}

func (hl *histlist) Show(i, width int) string {
	return ForceWcWidth(hl.filtered[i], width)
}

func (hl *histlist) Filter(filter string) int {
	hl.filtered = nil
	for _, item := range hl.all {
		if util.MatchSubseq(item, filter) {
			hl.filtered = append(hl.filtered, item)
		}
	}
	// Select the last entry.
	return len(hl.filtered) - 1
}

func (hl *histlist) Accept(i int, ed *Editor) {
	line := hl.all[i]
	if len(ed.line) > 0 {
		line = "\n" + line
	}
	ed.insertAtDot(line)
}

func (hl *histlist) ModeTitle(i int) string {
	return fmt.Sprintf(" HISTORY #%d ", i)
}

func startHistoryListing(ed *Editor) {
	hl, err := newHistlist(ed.store)
	if err != nil {
		ed.notify("%v", err)
		return
	}

	ed.histlist = newListing(modeHistoryListing, hl)
	ed.mode = &ed.histlist
}

func newHistlist(s *store.Store) (*histlist, error) {
	if s == nil {
		return nil, ErrStoreOffline
	}
	seq, err := s.NextCmdSeq()
	if err != nil {
		return nil, err
	}
	all, err := s.Cmds(0, seq)
	if err != nil {
		return nil, err
	}
	return &histlist{all, nil}, nil
}
<commit_msg>Only show first line of multiline history in histlist.<commit_after>package edit

import (
	"errors"
	"fmt"
	"strings"

	"github.com/elves/elvish/store"
	"github.com/elves/elvish/util"
)

// Command history listing mode.

var ErrStoreOffline = errors.New("store offline")

type histlist struct {
	all      []string
	filtered []string
}

func (hl *histlist) Len() int {
	return len(hl.filtered)
}

func (hl *histlist) Show(i, width int) string {
	entry := hl.filtered[i]
	if i := strings.IndexRune(entry, '\n'); i != -1 {
		entry = entry[:i] + "…"
	}
	return ForceWcWidth(entry, width)
}

func (hl *histlist) Filter(filter string) int {
	hl.filtered = nil
	for _, item := range hl.all {
		if util.MatchSubseq(item, filter) {
			hl.filtered = append(hl.filtered, item)
		}
	}
	// Select the last entry.
	return len(hl.filtered) - 1
}

func (hl *histlist) Accept(i int, ed *Editor) {
	line := hl.all[i]
	if len(ed.line) > 0 {
		line = "\n" + line
	}
	ed.insertAtDot(line)
}

func (hl *histlist) ModeTitle(i int) string {
	return fmt.Sprintf(" HISTORY #%d ", i)
}

func startHistoryListing(ed *Editor) {
	hl, err := newHistlist(ed.store)
	if err != nil {
		ed.notify("%v", err)
		return
	}

	ed.histlist = newListing(modeHistoryListing, hl)
	ed.mode = &ed.histlist
}

func newHistlist(s *store.Store) (*histlist, error) {
	if s == nil {
		return nil, ErrStoreOffline
	}
	seq, err := s.NextCmdSeq()
	if err != nil {
		return nil, err
	}
	all, err := s.Cmds(0, seq)
	if err != nil {
		return nil, err
	}
	return &histlist{all, nil}, nil
}
<|endoftext|>


<commit_before>package edit

import (
	"bytes"
	"fmt"
	"math"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/elves/elvish/edit/ui"
	"github.com/elves/elvish/eval"
	"github.com/elves/elvish/parse"
	"github.com/elves/elvish/store/storedefs"
	"github.com/elves/elvish/util"
)

// Location mode.

var _ = registerBuiltins(modeLocation, map[string]func(*Editor){
	"start": locStart,
})

func init() {
	registerBindings(modeLocation, modeLocation, map[ui.Key]string{})
}

// PinnedScore is a special value of Score in storedefs.Dir to represent that the
// directory is pinned.
var PinnedScore = math.Inf(1)

type location struct {
	home     string // The home directory; leave empty if unknown.
	all      []storedefs.Dir
	filtered []storedefs.Dir
}

func newLocation(dirs []storedefs.Dir, home string) *listing {
	l := newListing(modeLocation, &location{all: dirs, home: home})
	return &l
}

func (loc *location) ModeTitle(i int) string {
	return " LOCATION "
}

func (*location) CursorOnModeLine() bool {
	return true
}

func (loc *location) Len() int {
	return len(loc.filtered)
}

func (loc *location) Show(i int) (string, ui.Styled) {
	var header string
	score := loc.filtered[i].Score
	if score == PinnedScore {
		header = "*"
	} else {
		header = fmt.Sprintf("%.0f", score)
	}
	return header, ui.Unstyled(showPath(loc.filtered[i].Path, loc.home))
}

func (loc *location) Filter(filter string) int {
	loc.filtered = nil
	pattern := makeLocationFilterPattern(filter)
	for _, item := range loc.all {
		if pattern.MatchString(showPath(item.Path, loc.home)) {
			loc.filtered = append(loc.filtered, item)
		}
	}

	if len(loc.filtered) == 0 {
		return -1
	}
	return 0
}

func showPath(path, home string) string {
	if home != "" && path == home {
		return "~"
	} else if home != "" && strings.HasPrefix(path, home+"/") {
		return "~/" + parse.Quote(path[len(home)+1:])
	} else {
		return parse.Quote(path)
	}
}

var emptyRegexp = regexp.MustCompile("")

func makeLocationFilterPattern(s string) *regexp.Regexp {
	var b bytes.Buffer
	b.WriteString(".*")
	segs := strings.Split(s, "/")
	for i, seg := range segs {
		if i > 0 {
			b.WriteString(".*/.*")
		}
		b.WriteString(regexp.QuoteMeta(seg))
	}
	b.WriteString(".*")
	p, err := regexp.Compile(b.String())
	if err != nil {
		logger.Printf("failed to compile regexp %q: %v", b.String(), err)
		return emptyRegexp
	}
	return p
}

func (ed *Editor) chdir(dir string) error {
	dir, err := filepath.Abs(dir)
	if err != nil {
		return err
	}
	err = os.Chdir(dir)
	if err == nil {
		store := ed.daemon
		store.Waits().Add(1)
		go func() {
			// XXX Error ignored.
			store.AddDir(dir, 1)
			store.Waits().Done()
			logger.Println("added dir to store:", dir)
		}()
	}
	return err
}

// Editor interface.

func (loc *location) Accept(i int, ed *Editor) {
	err := ed.chdir(loc.filtered[i].Path)
	if err != nil {
		ed.Notify("%v", err)
	}
	ed.mode = &ed.insert
}

func locStart(ed *Editor) {
	if ed.daemon == nil {
		ed.Notify("%v", ErrStoreOffline)
		return
	}

	// Pinned directories are also blacklisted to prevent them from showing up
	// twice.
	black := convertListsToSet(ed.locHidden(), ed.locPinned())
	pwd, _ := os.Getwd()
	black[pwd] = struct{}{}
	stored, err := ed.daemon.Dirs(black)
	if err != nil {
		ed.Notify("store error: %v", err)
		return
	}

	// Concatenate pinned and stored dirs, pinned first.
	pinned := convertListToDirs(ed.locPinned())
	dirs := make([]storedefs.Dir, len(pinned)+len(stored))
	copy(dirs, pinned)
	copy(dirs[len(pinned):], stored)

	// Drop the error. When there is an error, home is "", which is used to
	// signify "no home known" in location.
	home, _ := util.GetHome("")
	ed.mode = newLocation(dirs, home)
}

// convertListToDirs converts a list of strings to []storedefs.Dir. It uses the
// special score of PinnedScore to signify that the directory is pinned.
func convertListToDirs(li eval.List) []storedefs.Dir {
	pinned := make([]storedefs.Dir, 0, li.Len())
	// XXX(xiaq): silently drops non-string items.
	li.Iterate(func(v eval.Value) bool {
		if s, ok := v.(eval.String); ok {
			pinned = append(pinned, storedefs.Dir{string(s), PinnedScore})
		}
		return true
	})
	return pinned
}

func convertListsToSet(lis ...eval.List) map[string]struct{} {
	set := make(map[string]struct{})
	// XXX(xiaq): silently drops non-string items.
	for _, li := range lis {
		li.Iterate(func(v eval.Value) bool {
			if s, ok := v.(eval.String); ok {
				set[string(s)] = struct{}{}
			}
			return true
		})
	}
	return set
}

// Variables.

var _ = RegisterVariable("loc-hidden", func() eval.Variable {
	return eval.NewPtrVariableWithValidator(eval.NewList(), eval.ShouldBeList)
})

func (ed *Editor) locHidden() eval.List {
	return ed.variables["loc-hidden"].Get().(eval.List)
}

var _ = RegisterVariable("loc-pinned", func() eval.Variable {
	return eval.NewPtrVariableWithValidator(eval.NewList(), eval.ShouldBeList)
})

func (ed *Editor) locPinned() eval.List {
	return ed.variables["loc-pinned"].Get().(eval.List)
}
<commit_msg>Adding error handling on retrieving the pwd #testing<commit_after>package edit

import (
	"bytes"
	"fmt"
	"math"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/elves/elvish/edit/ui"
	"github.com/elves/elvish/eval"
	"github.com/elves/elvish/parse"
	"github.com/elves/elvish/store/storedefs"
	"github.com/elves/elvish/util"
)

// Location mode.

var _ = registerBuiltins(modeLocation, map[string]func(*Editor){
	"start": locStart,
})

func init() {
	registerBindings(modeLocation, modeLocation, map[ui.Key]string{})
}

// PinnedScore is a special value of Score in storedefs.Dir to represent that the
// directory is pinned.
var PinnedScore = math.Inf(1)

type location struct {
	home     string // The home directory; leave empty if unknown.
	all      []storedefs.Dir
	filtered []storedefs.Dir
}

func newLocation(dirs []storedefs.Dir, home string) *listing {
	l := newListing(modeLocation, &location{all: dirs, home: home})
	return &l
}

func (loc *location) ModeTitle(i int) string {
	return " LOCATION "
}

func (*location) CursorOnModeLine() bool {
	return true
}

func (loc *location) Len() int {
	return len(loc.filtered)
}

func (loc *location) Show(i int) (string, ui.Styled) {
	var header string
	score := loc.filtered[i].Score
	if score == PinnedScore {
		header = "*"
	} else {
		header = fmt.Sprintf("%.0f", score)
	}
	return header, ui.Unstyled(showPath(loc.filtered[i].Path, loc.home))
}

func (loc *location) Filter(filter string) int {
	loc.filtered = nil
	pattern := makeLocationFilterPattern(filter)
	for _, item := range loc.all {
		if pattern.MatchString(showPath(item.Path, loc.home)) {
			loc.filtered = append(loc.filtered, item)
		}
	}

	if len(loc.filtered) == 0 {
		return -1
	}
	return 0
}

func showPath(path, home string) string {
	if home != "" && path == home {
		return "~"
	} else if home != "" && strings.HasPrefix(path, home+"/") {
		return "~/" + parse.Quote(path[len(home)+1:])
	} else {
		return parse.Quote(path)
	}
}

var emptyRegexp = regexp.MustCompile("")

func makeLocationFilterPattern(s string) *regexp.Regexp {
	var b bytes.Buffer
	b.WriteString(".*")
	segs := strings.Split(s, "/")
	for i, seg := range segs {
		if i > 0 {
			b.WriteString(".*/.*")
		}
		b.WriteString(regexp.QuoteMeta(seg))
	}
	b.WriteString(".*")
	p, err := regexp.Compile(b.String())
	if err != nil {
		logger.Printf("failed to compile regexp %q: %v", b.String(), err)
		return emptyRegexp
	}
	return p
}

func (ed *Editor) chdir(dir string) error {
	dir, err := filepath.Abs(dir)
	if err != nil {
		return err
	}
	err = os.Chdir(dir)
	if err == nil {
		store := ed.daemon
		store.Waits().Add(1)
		go func() {
			// XXX Error ignored.
			store.AddDir(dir, 1)
			store.Waits().Done()
			logger.Println("added dir to store:", dir)
		}()
	}
	return err
}

// Editor interface.

func (loc *location) Accept(i int, ed *Editor) {
	err := ed.chdir(loc.filtered[i].Path)
	if err != nil {
		ed.Notify("%v", err)
	}
	ed.mode = &ed.insert
}

func locStart(ed *Editor) {
	if ed.daemon == nil {
		ed.Notify("%v", ErrStoreOffline)
		return
	}

	// Pinned directories are also blacklisted to prevent them from showing up
	// twice.
	black := convertListsToSet(ed.locHidden(), ed.locPinned())
	pwd, err := os.Getwd()
	if err == nil {
		black[pwd] = struct{}{}
	}
	stored, err := ed.daemon.Dirs(black)
	if err != nil {
		ed.Notify("store error: %v", err)
		return
	}

	// Concatenate pinned and stored dirs, pinned first.
	pinned := convertListToDirs(ed.locPinned())
	dirs := make([]storedefs.Dir, len(pinned)+len(stored))
	copy(dirs, pinned)
	copy(dirs[len(pinned):], stored)

	// Drop the error. When there is an error, home is "", which is used to
	// signify "no home known" in location.
	home, _ := util.GetHome("")
	ed.mode = newLocation(dirs, home)
}

// convertListToDirs converts a list of strings to []storedefs.Dir. It uses the
// special score of PinnedScore to signify that the directory is pinned.
func convertListToDirs(li eval.List) []storedefs.Dir {
	pinned := make([]storedefs.Dir, 0, li.Len())
	// XXX(xiaq): silently drops non-string items.
	li.Iterate(func(v eval.Value) bool {
		if s, ok := v.(eval.String); ok {
			pinned = append(pinned, storedefs.Dir{string(s), PinnedScore})
		}
		return true
	})
	return pinned
}

func convertListsToSet(lis ...eval.List) map[string]struct{} {
	set := make(map[string]struct{})
	// XXX(xiaq): silently drops non-string items.
	for _, li := range lis {
		li.Iterate(func(v eval.Value) bool {
			if s, ok := v.(eval.String); ok {
				set[string(s)] = struct{}{}
			}
			return true
		})
	}
	return set
}

// Variables.

var _ = RegisterVariable("loc-hidden", func() eval.Variable {
	return eval.NewPtrVariableWithValidator(eval.NewList(), eval.ShouldBeList)
})

func (ed *Editor) locHidden() eval.List {
	return ed.variables["loc-hidden"].Get().(eval.List)
}

var _ = RegisterVariable("loc-pinned", func() eval.Variable {
	return eval.NewPtrVariableWithValidator(eval.NewList(), eval.ShouldBeList)
})

func (ed *Editor) locPinned() eval.List {
	return ed.variables["loc-pinned"].Get().(eval.List)
}
<|endoftext|>


<commit_before>// Package editor provides a graphical, editable text area widget.
package editor // import "sigint.ca/graphics/editor"

import (
	"image"
	"time"

	"sigint.ca/clip"
	"sigint.ca/graphics/editor/internal/hist"
	"sigint.ca/graphics/editor/internal/text"

	"golang.org/x/image/font"
	"golang.org/x/mobile/event/key"
	"golang.org/x/mobile/event/mouse"
)

// An Editor is a graphical, editable text area widget, intended to be
// compatible with golang.org/x/exp/shiny, or any other graphical
// window package capable of drawing a widget via an image.RGBA.
// See sigint.ca/cmd/edit for an example program using this type.
type Editor struct {
	// textual state
	buf *text.Buffer
	dot text.Selection // the current selection

	// images and drawing data
	img      *image.RGBA
	scrollPt image.Point
	dirty    bool

	// configurable
	font   fontface
	bgcol  *image.Uniform
	selcol *image.Uniform
	cursor func(height int) image.Image
	margin image.Point

	// history
	history     *hist.History        // represents the Editor's history
	savePoint   *hist.Transformation // records the last time the Editor was saved, for use by Saved and SetSaved
	uncommitted *hist.Transformation // recent input which hasn't yet been committed to history

	// mouse related state
	lastClickTime time.Time    // used to detect a double-click
	sweepOrigin   text.Address // the origin of a sweep
	sweepLast     text.Address // the last column that was swept

	clipboard *clip.Clipboard // the clipboard to be used for copy or paste events
}

// NewEditor returns a new Editor with a clipping rectangle defined by size, a font face
// defined by face and height, and an OptionSet opts. If opts is nil, editor.SimpleTheme
// will be used.
func NewEditor(size image.Point, face font.Face, opts *OptionSet) *Editor {
	if opts == nil {
		opts = SimpleTheme
	}
	ed := &Editor{
		buf: text.NewBuffer(),

		img:   image.NewRGBA(image.Rectangle{Max: size}),
		dirty: true,

		font: mkFont(face),

		bgcol:  image.NewUniform(opts.BGColor),
		selcol: image.NewUniform(opts.SelColor),
		cursor: opts.Cursor,
		margin: opts.Margin,

		history:   new(hist.History),
		clipboard: new(clip.Clipboard),
	}
	return ed
}

func (ed *Editor) GetFont() font.Face {
	return ed.font.face
}

func (ed *Editor) SetFont(face font.Face) {
	ed.font = mkFont(face)
	ed.dirty = true
}

func (ed *Editor) GetOpts() *OptionSet {
	return &OptionSet{
		BGColor:  ed.bgcol.C,
		SelColor: ed.selcol.C,
		Cursor:   ed.cursor,
		Margin:   ed.margin,
	}
}

func (ed *Editor) SetOpts(opts *OptionSet) {
	ed.bgcol = image.NewUniform(opts.BGColor)
	ed.selcol = image.NewUniform(opts.SelColor)
	ed.cursor = opts.Cursor
	ed.margin = opts.Margin
	ed.dirty = true
}

func (ed *Editor) Release() {
}

func (ed *Editor) Bounds() image.Rectangle {
	return ed.img.Bounds()
}

func (ed *Editor) Size() image.Point {
	return ed.Bounds().Size()
}

// Resize resizes the Editor. Subsequent calls to RGBA will return an image of
// the given size.
func (ed *Editor) Resize(size image.Point) {
	r := image.Rectangle{Max: size}
	ed.img = image.NewRGBA(r)
	ed.dirty = true
}

// RGBA returns an image representing the current state of the Editor.
func (ed *Editor) RGBA() (img *image.RGBA) {
	if ed.dirty {
		ed.redraw()
		ed.dirty = false
	}
	return ed.img
}

// Dirty reports whether the next call to RGBA will result in a different
// image than the previous call.
func (ed *Editor) Dirty() bool {
	return ed.dirty
}

// Contents returns the contents of the Editor.
func (ed *Editor) Contents() []byte {
	return ed.buf.Contents()
}

// Load replaces the contents of the Editor with s, and
// resets the Editor's history.
func (ed *Editor) Load(s []byte) {
	ed.buf.ClearSel(ed.dot)
	ed.buf.InsertString(text.Address{0, 0}, string(s))
	ed.history = new(hist.History)
	ed.dot = text.Selection{}
	ed.dirty = true
}

// SetSaved instructs the Editor that the current contents should be
// considered saved. After calling SetSaved, the client can call
// Saved to see if the Editor has unsaved content.
func (ed *Editor) SetSaved() {
	// TODO: ensure ed.uncommitted is empty?
	if ed.uncommitted != nil {
		panic("TODO")
	}
	ed.savePoint = ed.history.Current()
}

// Saved reports whether the Editor has been modified since the last
// time SetSaved was called.
func (ed *Editor) Saved() bool {
	return ed.history.Current() == ed.savePoint && ed.uncommitted == nil
}

// SendKeyEvent sends a key event to be interpreted by the Editor.
func (ed *Editor) SendKeyEvent(e key.Event) {
	ed.handleKeyEvent(e)
}

// SendMouseEvent sends a mouse event to be interpreted by the Editor.
func (ed *Editor) SendMouseEvent(e mouse.Event) {
	ed.handleMouseEvent(e)
}

// SendScrollEvent sends a scroll event to be interpreted by the Editor.
func (ed *Editor) SendScrollEvent(e mouse.ScrollEvent) {
	var pt image.Point
	if e.Precise {
		pt.X = int(e.Dx)
		pt.Y = int(e.Dy)
	} else {
		pt.X = int(e.Dx * float32(ed.font.height))
		pt.Y = int(e.Dy * float32(ed.font.height))
	}
	oldPt := ed.scrollPt
	ed.scroll(pt)
	if ed.scrollPt != oldPt {
		ed.dirty = true
	}
}
<commit_msg>editor: remove unnecessary API<commit_after>// Package editor provides a graphical, editable text area widget.
package editor // import "sigint.ca/graphics/editor"

import (
	"image"
	"time"

	"sigint.ca/clip"
	"sigint.ca/graphics/editor/internal/hist"
	"sigint.ca/graphics/editor/internal/text"

	"golang.org/x/image/font"
	"golang.org/x/mobile/event/key"
	"golang.org/x/mobile/event/mouse"
)

// An Editor is a graphical, editable text area widget, intended to be
// compatible with golang.org/x/exp/shiny, or any other graphical
// window package capable of drawing a widget via an image.RGBA.
// See sigint.ca/cmd/edit for an example program using this type.
type Editor struct {
	// textual state
	buf *text.Buffer
	dot text.Selection // the current selection

	// images and drawing data
	img      *image.RGBA
	scrollPt image.Point
	dirty    bool

	// configurable
	font   fontface
	bgcol  *image.Uniform
	selcol *image.Uniform
	cursor func(height int) image.Image
	margin image.Point

	// history
	history     *hist.History        // represents the Editor's history
	savePoint   *hist.Transformation // records the last time the Editor was saved, for use by Saved and SetSaved
	uncommitted *hist.Transformation // recent input which hasn't yet been committed to history

	// mouse related state
	lastClickTime time.Time    // used to detect a double-click
	sweepOrigin   text.Address // the origin of a sweep
	sweepLast     text.Address // the last column that was swept

	clipboard *clip.Clipboard // the clipboard to be used for copy or paste events
}

// NewEditor returns a new Editor with a clipping rectangle defined by size, a font face
// defined by face and height, and an OptionSet opts. If opts is nil, editor.SimpleTheme
// will be used.
func NewEditor(size image.Point, face font.Face, opts *OptionSet) *Editor {
	if opts == nil {
		opts = SimpleTheme
	}
	ed := &Editor{
		buf: text.NewBuffer(),

		img:   image.NewRGBA(image.Rectangle{Max: size}),
		dirty: true,

		font: mkFont(face),

		bgcol:  image.NewUniform(opts.BGColor),
		selcol: image.NewUniform(opts.SelColor),
		cursor: opts.Cursor,
		margin: opts.Margin,

		history:   new(hist.History),
		clipboard: new(clip.Clipboard),
	}
	return ed
}

func (ed *Editor) GetFont() font.Face {
	return ed.font.face
}

func (ed *Editor) SetFont(face font.Face) {
	ed.font = mkFont(face)
	ed.dirty = true
}

func (ed *Editor) GetOpts() *OptionSet {
	return &OptionSet{
		BGColor:  ed.bgcol.C,
		SelColor: ed.selcol.C,
		Cursor:   ed.cursor,
		Margin:   ed.margin,
	}
}

func (ed *Editor) SetOpts(opts *OptionSet) {
	ed.bgcol = image.NewUniform(opts.BGColor)
	ed.selcol = image.NewUniform(opts.SelColor)
	ed.cursor = opts.Cursor
	ed.margin = opts.Margin
	ed.dirty = true
}

func (ed *Editor) Bounds() image.Rectangle {
	return ed.img.Bounds()
}

// Resize resizes the Editor. Subsequent calls to RGBA will return an image of
// the given size.
func (ed *Editor) Resize(size image.Point) {
	r := image.Rectangle{Max: size}
	ed.img = image.NewRGBA(r)
	ed.dirty = true
}

// RGBA returns an image representing the current state of the Editor.
func (ed *Editor) RGBA() (img *image.RGBA) {
	if ed.dirty {
		ed.redraw()
		ed.dirty = false
	}
	return ed.img
}

// Dirty reports whether the next call to RGBA will result in a different
// image than the previous call.
func (ed *Editor) Dirty() bool {
	return ed.dirty
}

// Contents returns the contents of the Editor.
func (ed *Editor) Contents() []byte {
	return ed.buf.Contents()
}

// Load replaces the contents of the Editor with s, and
// resets the Editor's history.
func (ed *Editor) Load(s []byte) {
	ed.buf.ClearSel(ed.dot)
	ed.buf.InsertString(text.Address{0, 0}, string(s))
	ed.history = new(hist.History)
	ed.dot = text.Selection{}
	ed.dirty = true
}

// SetSaved instructs the Editor that the current contents should be
// considered saved. After calling SetSaved, the client can call
// Saved to see if the Editor has unsaved content.
func (ed *Editor) SetSaved() {
	// TODO: ensure ed.uncommitted is empty?
	if ed.uncommitted != nil {
		panic("TODO")
	}
	ed.savePoint = ed.history.Current()
}

// Saved reports whether the Editor has been modified since the last
// time SetSaved was called.
func (ed *Editor) Saved() bool {
	return ed.history.Current() == ed.savePoint && ed.uncommitted == nil
}

// SendKeyEvent sends a key event to be interpreted by the Editor.
func (ed *Editor) SendKeyEvent(e key.Event) {
	ed.handleKeyEvent(e)
}

// SendMouseEvent sends a mouse event to be interpreted by the Editor.
func (ed *Editor) SendMouseEvent(e mouse.Event) {
	ed.handleMouseEvent(e)
}

// SendScrollEvent sends a scroll event to be interpreted by the Editor.
func (ed *Editor) SendScrollEvent(e mouse.ScrollEvent) {
	var pt image.Point
	if e.Precise {
		pt.X = int(e.Dx)
		pt.Y = int(e.Dy)
	} else {
		pt.X = int(e.Dx * float32(ed.font.height))
		pt.Y = int(e.Dy * float32(ed.font.height))
	}
	oldPt := ed.scrollPt
	ed.scroll(pt)
	if ed.scrollPt != oldPt {
		ed.dirty = true
	}
}
<|endoftext|>