<commit_before>/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package flogging

import (
	"fmt"
	"io"
	"os"
	"sync"

	"github.com/hyperledger/fabric/common/flogging/fabenc"
	logging "github.com/op/go-logging"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

// Config is used to provide dependencies to a Logging instance.
type Config struct {
	// Format is the log record format specifier for the Logging instance. If the
	// spec is the string "json", log records will be formatted as JSON. Any
	// other string will be provided to the FormatEncoder. Please see
	// fabenc.ParseFormat for details on the supported verbs.
	//
	// If Format is not provided, a default format that provides basic information will
	// be used.
	Format string

	// LogSpec determines the log levels that are enabled for the logging system. The
	// spec must be in a format that can be processed by ActivateSpec.
	//
	// If LogSpec is not provided, loggers will be enabled at the INFO level.
	LogSpec string

	// Writer is the sink for encoded and formatted log records.
	//
	// If a Writer is not provided, os.Stderr will be used as the log sink.
	Writer io.Writer
}

// Logging maintains the state associated with the fabric logging system. It is
// intended to bridge between the legacy logging infrastructure built around
// go-logging and the structured, level logging provided by zap.
type Logging struct {
	*LoggerLevels

	mutex          sync.RWMutex
	encoding       Encoding
	encoderConfig  zapcore.EncoderConfig
	multiFormatter *fabenc.MultiFormatter
	writer         zapcore.WriteSyncer
	observer       Observer
}

// New creates a new logging system and initializes it with the provided
// configuration.
func New(c Config) (*Logging, error) {
	encoderConfig := zap.NewProductionEncoderConfig()
	encoderConfig.NameKey = "name"

	s := &Logging{
		LoggerLevels: &LoggerLevels{
			defaultLevel: defaultLevel,
		},
		encoderConfig:  encoderConfig,
		multiFormatter: fabenc.NewMultiFormatter(),
	}

	err := s.Apply(c)
	if err != nil {
		return nil, err
	}
	return s, nil
}

// Apply applies the provided configuration to the logging system.
func (s *Logging) Apply(c Config) error {
	err := s.SetFormat(c.Format)
	if err != nil {
		return err
	}

	if c.LogSpec == "" {
		c.LogSpec = os.Getenv("FABRIC_LOGGING_SPEC")
	}
	if c.LogSpec == "" {
		c.LogSpec = defaultLevel.String()
	}

	err = s.LoggerLevels.ActivateSpec(c.LogSpec)
	if err != nil {
		return err
	}

	if c.Writer == nil {
		c.Writer = os.Stderr
	}
	s.SetWriter(c.Writer)

	var formatter logging.Formatter
	if s.Encoding() == JSON {
		formatter = SetFormat(defaultFormat)
	} else {
		formatter = SetFormat(c.Format)
	}

	InitBackend(formatter, c.Writer)

	return nil
}

// SetFormat updates how log records are formatted and encoded. Log entries
// created after this method has completed will use the new format.
//
// An error is returned if the log format specification cannot be parsed.
func (s *Logging) SetFormat(format string) error {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	if format == "" {
		format = defaultFormat
	}

	if format == "json" {
		s.encoding = JSON
		return nil
	}

	formatters, err := fabenc.ParseFormat(format)
	if err != nil {
		return err
	}
	s.multiFormatter.SetFormatters(formatters)
	s.encoding = CONSOLE

	return nil
}

// SetWriter controls which writer formatted log records are written to.
// Writers, with the exception of an *os.File, need to be safe for concurrent
// use by multiple go routines.
func (s *Logging) SetWriter(w io.Writer) {
	var sw zapcore.WriteSyncer
	switch t := w.(type) {
	case *os.File:
		sw = zapcore.Lock(t)
	case zapcore.WriteSyncer:
		sw = t
	default:
		sw = zapcore.AddSync(w)
	}

	s.mutex.Lock()
	s.writer = sw
	s.mutex.Unlock()
}

// Write satisfies the io.Write contract. It delegates to the writer argument
// of SetWriter or the Writer field of Config. The Core uses this when encoding
// log records.
func (s *Logging) Write(b []byte) (int, error) {
	s.mutex.RLock()
	w := s.writer
	s.mutex.RUnlock()

	return w.Write(b)
}

// Sync satisfies the zapcore.WriteSyncer interface. It is used by the Core to
// flush log records before terminating the process.
func (s *Logging) Sync() error {
	s.mutex.RLock()
	w := s.writer
	s.mutex.RUnlock()

	return w.Sync()
}

// Encoding satisfies the Encoding interface. It determines whether the JSON or
// CONSOLE encoder should be used by the Core when log records are written.
func (s *Logging) Encoding() Encoding {
	s.mutex.RLock()
	e := s.encoding
	s.mutex.RUnlock()
	return e
}

// ZapLogger instantiates a new zap.Logger with the specified name. The name is
// used to determine which log levels are enabled.
func (s *Logging) ZapLogger(name string) *zap.Logger {
	if !isValidLoggerName(name) {
		panic(fmt.Sprintf("invalid logger name: %s", name))
	}

	// always return true here because the core's Check()
	// method computes the level for the logger name based
	// on the active logging spec
	levelEnabler := zap.LevelEnablerFunc(func(l zapcore.Level) bool { return true })

	s.mutex.RLock()
	core := &Core{
		LevelEnabler: levelEnabler,
		Levels:       s.LoggerLevels,
		Encoders: map[Encoding]zapcore.Encoder{
			JSON:    zapcore.NewJSONEncoder(s.encoderConfig),
			CONSOLE: fabenc.NewFormatEncoder(s.multiFormatter),
		},
		Selector: s,
		Output:   s,
		Observer: s,
	}
	s.mutex.RUnlock()

	return NewZapLogger(core).Named(name)
}

func (s *Logging) Check(e zapcore.Entry, ce *zapcore.CheckedEntry) {
	s.mutex.RLock()
	if s.observer != nil {
		s.observer.Check(e, ce)
	}
	s.mutex.RUnlock()
}

func (s *Logging) WriteEntry(e zapcore.Entry, fields []zapcore.Field) {
	s.mutex.RLock()
	if s.observer != nil {
		s.observer.WriteEntry(e, fields)
	}
	s.mutex.RUnlock()
}

func (s *Logging) SetObserver(observer Observer) {
	s.mutex.Lock()
	s.observer = observer
	s.mutex.Unlock()
}

// Logger instantiates a new FabricLogger with the specified name. The name is
// used to determine which log levels are enabled.
func (s *Logging) Logger(name string) *FabricLogger {
	zl := s.ZapLogger(name)
	return NewFabricLogger(zl)
}
<commit_msg>[FAB-13591] reduce scope of rlock around observer<commit_after>/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package flogging

import (
	"fmt"
	"io"
	"os"
	"sync"

	"github.com/hyperledger/fabric/common/flogging/fabenc"
	logging "github.com/op/go-logging"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

// Config is used to provide dependencies to a Logging instance.
type Config struct {
	// Format is the log record format specifier for the Logging instance. If the
	// spec is the string "json", log records will be formatted as JSON. Any
	// other string will be provided to the FormatEncoder. Please see
	// fabenc.ParseFormat for details on the supported verbs.
	//
	// If Format is not provided, a default format that provides basic information will
	// be used.
	Format string

	// LogSpec determines the log levels that are enabled for the logging system. The
	// spec must be in a format that can be processed by ActivateSpec.
	//
	// If LogSpec is not provided, loggers will be enabled at the INFO level.
	LogSpec string

	// Writer is the sink for encoded and formatted log records.
	//
	// If a Writer is not provided, os.Stderr will be used as the log sink.
	Writer io.Writer
}

// Logging maintains the state associated with the fabric logging system. It is
// intended to bridge between the legacy logging infrastructure built around
// go-logging and the structured, level logging provided by zap.
type Logging struct {
	*LoggerLevels

	mutex          sync.RWMutex
	encoding       Encoding
	encoderConfig  zapcore.EncoderConfig
	multiFormatter *fabenc.MultiFormatter
	writer         zapcore.WriteSyncer
	observer       Observer
}

// New creates a new logging system and initializes it with the provided
// configuration.
func New(c Config) (*Logging, error) {
	encoderConfig := zap.NewProductionEncoderConfig()
	encoderConfig.NameKey = "name"

	s := &Logging{
		LoggerLevels: &LoggerLevels{
			defaultLevel: defaultLevel,
		},
		encoderConfig:  encoderConfig,
		multiFormatter: fabenc.NewMultiFormatter(),
	}

	err := s.Apply(c)
	if err != nil {
		return nil, err
	}
	return s, nil
}

// Apply applies the provided configuration to the logging system.
func (s *Logging) Apply(c Config) error {
	err := s.SetFormat(c.Format)
	if err != nil {
		return err
	}

	if c.LogSpec == "" {
		c.LogSpec = os.Getenv("FABRIC_LOGGING_SPEC")
	}
	if c.LogSpec == "" {
		c.LogSpec = defaultLevel.String()
	}

	err = s.LoggerLevels.ActivateSpec(c.LogSpec)
	if err != nil {
		return err
	}

	if c.Writer == nil {
		c.Writer = os.Stderr
	}
	s.SetWriter(c.Writer)

	var formatter logging.Formatter
	if s.Encoding() == JSON {
		formatter = SetFormat(defaultFormat)
	} else {
		formatter = SetFormat(c.Format)
	}

	InitBackend(formatter, c.Writer)

	return nil
}

// SetFormat updates how log records are formatted and encoded. Log entries
// created after this method has completed will use the new format.
//
// An error is returned if the log format specification cannot be parsed.
func (s *Logging) SetFormat(format string) error {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	if format == "" {
		format = defaultFormat
	}

	if format == "json" {
		s.encoding = JSON
		return nil
	}

	formatters, err := fabenc.ParseFormat(format)
	if err != nil {
		return err
	}
	s.multiFormatter.SetFormatters(formatters)
	s.encoding = CONSOLE

	return nil
}

// SetWriter controls which writer formatted log records are written to.
// Writers, with the exception of an *os.File, need to be safe for concurrent
// use by multiple go routines.
func (s *Logging) SetWriter(w io.Writer) {
	var sw zapcore.WriteSyncer
	switch t := w.(type) {
	case *os.File:
		sw = zapcore.Lock(t)
	case zapcore.WriteSyncer:
		sw = t
	default:
		sw = zapcore.AddSync(w)
	}

	s.mutex.Lock()
	s.writer = sw
	s.mutex.Unlock()
}

// SetObserver is used to provide a log observer that will be called as log
// levels are checked or written.. Only a single observer is supported.
func (s *Logging) SetObserver(observer Observer) {
	s.mutex.Lock()
	s.observer = observer
	s.mutex.Unlock()
}

// Write satisfies the io.Write contract. It delegates to the writer argument
// of SetWriter or the Writer field of Config. The Core uses this when encoding
// log records.
func (s *Logging) Write(b []byte) (int, error) {
	s.mutex.RLock()
	w := s.writer
	s.mutex.RUnlock()

	return w.Write(b)
}

// Sync satisfies the zapcore.WriteSyncer interface. It is used by the Core to
// flush log records before terminating the process.
func (s *Logging) Sync() error {
	s.mutex.RLock()
	w := s.writer
	s.mutex.RUnlock()

	return w.Sync()
}

// Encoding satisfies the Encoding interface. It determines whether the JSON or
// CONSOLE encoder should be used by the Core when log records are written.
func (s *Logging) Encoding() Encoding {
	s.mutex.RLock()
	e := s.encoding
	s.mutex.RUnlock()
	return e
}

// ZapLogger instantiates a new zap.Logger with the specified name. The name is
// used to determine which log levels are enabled.
func (s *Logging) ZapLogger(name string) *zap.Logger {
	if !isValidLoggerName(name) {
		panic(fmt.Sprintf("invalid logger name: %s", name))
	}

	// always return true here because the core's Check()
	// method computes the level for the logger name based
	// on the active logging spec
	levelEnabler := zap.LevelEnablerFunc(func(l zapcore.Level) bool { return true })

	s.mutex.RLock()
	core := &Core{
		LevelEnabler: levelEnabler,
		Levels:       s.LoggerLevels,
		Encoders: map[Encoding]zapcore.Encoder{
			JSON:    zapcore.NewJSONEncoder(s.encoderConfig),
			CONSOLE: fabenc.NewFormatEncoder(s.multiFormatter),
		},
		Selector: s,
		Output:   s,
		Observer: s,
	}
	s.mutex.RUnlock()

	return NewZapLogger(core).Named(name)
}

func (s *Logging) Check(e zapcore.Entry, ce *zapcore.CheckedEntry) {
	s.mutex.RLock()
	observer := s.observer
	s.mutex.RUnlock()

	if observer != nil {
		observer.Check(e, ce)
	}
}

func (s *Logging) WriteEntry(e zapcore.Entry, fields []zapcore.Field) {
	s.mutex.RLock()
	observer := s.observer
	s.mutex.RUnlock()

	if observer != nil {
		observer.WriteEntry(e, fields)
	}
}

// Logger instantiates a new FabricLogger with the specified name. The name is
// used to determine which log levels are enabled.
func (s *Logging) Logger(name string) *FabricLogger {
	zl := s.ZapLogger(name)
	return NewFabricLogger(zl)
}
<|endoftext|>


<commit_before>// Copyright 2013 Unknown
//
// 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.

// Go Walker Local is the local version of Go Walker as an alternative of godoc.
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"runtime"
	"strconv"

	"github.com/Unknwon/gowalker/utils"
)

const (
	APP_VER = "0.0.2.0823"
)

var (
	appPath  string
	httpPort = 8082
)

func init() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	// Set App version.
	// routers.AppVer = "v" + APP_VER
}

func main() {
	utils.ColorLog("[INFO] Go Walker Local v%s.\n", APP_VER)

	var err error
	appPath, err = utils.GetAppPath("github.com/Unknwon/gowalker/gw-local", "conf")
	if err != nil {
		utils.ColorLog("[ERRO] Cannot assign 'appPath'[ %s ]\n", err)
		return
	}

	utils.ColorLog("[INFO] File server( %s )\n", appPath)

	// Get 'args'.
	args := os.Args[1:]
	if len(args) > 0 {
		hp, err := strconv.Atoi(args[0])
		if err == nil {
			httpPort = hp
		}
	}

	http.HandleFunc("/", serverHome)
	http.HandleFunc("/index", serverIndex)
	http.HandleFunc("/funcs", serverFuncs)
	http.HandleFunc("/about", serverAbout)

	http.ListenAndServe(":"+fmt.Sprint(httpPort), nil)
}

func serverHome(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, "Home page")
}

func serverIndex(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, "Index")
}

func serverFuncs(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, "Funcs")
}

func serverAbout(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, "About")
}
<commit_msg>Init gw-local handlers<commit_after>// Copyright 2013 Unknown
//
// 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.

// Go Walker Local is the local version of Go Walker as an alternative of godoc.
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"runtime"
	"strconv"

	"github.com/Unknwon/gowalker/utils"
)

const (
	APP_VER = "0.0.2.0823"
)

var (
	appPath  string
	httpPort = 8082
)

func init() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	// Set App version.
}

func main() {
	utils.ColorLog("[INFO] Go Walker Local v%s.\n", APP_VER)

	var err error
	appPath, err = utils.GetAppPath("github.com/Unknwon/gowalker/gw-local", "conf")
	if err != nil {
		utils.ColorLog("[ERRO] Cannot assign 'appPath'[ %s ]\n", err)
		return
	}

	utils.ColorLog("[INFO] File server( %s )\n", appPath)

	// Get 'args'.
	args := os.Args[1:]
	if len(args) > 0 {
		hp, err := strconv.Atoi(args[0])
		if err == nil {
			httpPort = hp
		}
	}

	http.HandleFunc("/", serverHome)
	http.HandleFunc("/index", serverIndex)
	http.HandleFunc("/funcs", serverFuncs)
	http.HandleFunc("/about", serverAbout)

	http.Handle("/static/", http.StripPrefix("/static/",
		http.FileServer(http.Dir(appPath+"static/"))))

	http.ListenAndServe(":"+fmt.Sprint(httpPort), nil)
}

func serverHome(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, "Home page")
}

func serverIndex(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, "Index")
}

func serverFuncs(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, "Funcs")
}

func serverAbout(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, "About")
}
<|endoftext|>


<commit_before>package buildlog

import (
	"fmt"
	"net/url"

	"github.com/openshift/origin/pkg/build/registry/build"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	kubeapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
)

// REST is an implementation of RESTStorage for the api server.
type REST struct {
	BuildRegistry build.Registry
	PodClient     client.PodInterface
	proxyPrefix   string
}

// NewREST creates a new REST for BuildLog
// Takes build registry and pod client to get neccessary attibutes to assamble
// URL to which the request shall be redirected in order to get build logs.
func NewREST(b build.Registry, c client.PodInterface, p string) apiserver.RESTStorage {
	return &REST{
		BuildRegistry: b,
		PodClient: c,
		proxyPrefix: p,
	}
}

// Redirector implementation
func (r *REST) ResourceLocation(id string) (string, error) {
	build, err := r.BuildRegistry.GetBuild(id)
	if err != nil {
		return "", fmt.Errorf("No such build")
	}

	pod, err := r.PodClient.GetPod(kubeapi.NewContext(), build.PodID)
	if err != nil {
		return "", fmt.Errorf("No such pod")
	}
	buildPodID := build.PodID
	buildHost  := pod.CurrentState.Host
	// Build will take place only in one container
	buildContainerName := pod.DesiredState.Manifest.Containers[0].Name
	location := &url.URL{
		Path: r.proxyPrefix + "/" + buildHost + "/containerLogs/" + buildPodID + "/" + buildContainerName,
	}
	if err != nil {
		return "", err
	}
	return location.String(), nil
}

func (r *REST) Get(ctx kubeapi.Context, id string) (runtime.Object, error) {
	return nil, fmt.Errorf("BuildLog can't be retrieved")
}

func (r *REST) New() runtime.Object {
	return nil
}

func (r *REST) List(ctx kubeapi.Context, selector, fields labels.Selector) (runtime.Object, error) {
	return nil, fmt.Errorf("BuildLog can't be listed")
}

func (r *REST) Delete(ctx kubeapi.Context, id string) (<-chan runtime.Object, error) {
	return nil, fmt.Errorf("BuildLog can't be deleted")
}

func (r *REST) Create(ctx kubeapi.Context, obj runtime.Object) (<-chan runtime.Object, error) {
	return nil, fmt.Errorf("BuildLog can't be created")
}

func (r *REST) Update(ctx kubeapi.Context, obj runtime.Object) (<-chan runtime.Object, error) {
	return nil, fmt.Errorf("BuildLog can't be updated")
}
<commit_msg>Fix /buildLogs<commit_after>package buildlog

import (
	"fmt"
	"net/url"

	kubeapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
	"github.com/openshift/origin/pkg/build/registry/build"
)

// REST is an implementation of RESTStorage for the api server.
type REST struct {
	BuildRegistry build.Registry
	PodClient     client.PodInterface
	proxyPrefix   string
}

// NewREST creates a new REST for BuildLog
// Takes build registry and pod client to get neccessary attibutes to assamble
// URL to which the request shall be redirected in order to get build logs.
func NewREST(b build.Registry, c client.PodInterface, p string) apiserver.RESTStorage {
	return &REST{
		BuildRegistry: b,
		PodClient:     c,
		proxyPrefix:   p,
	}
}

// Redirector implementation
func (r *REST) ResourceLocation(ctx kubeapi.Context, id string) (string, error) {
	build, err := r.BuildRegistry.GetBuild(id)
	if err != nil {
		return "", fmt.Errorf("No such build")
	}

	pod, err := r.PodClient.GetPod(kubeapi.NewContext(), build.PodID)
	if err != nil {
		return "", fmt.Errorf("No such pod")
	}
	buildPodID := build.PodID
	buildHost := pod.CurrentState.Host
	// Build will take place only in one container
	buildContainerName := pod.DesiredState.Manifest.Containers[0].Name
	location := &url.URL{
		Path: r.proxyPrefix + "/" + buildHost + "/containerLogs/" + buildPodID + "/" + buildContainerName,
	}
	if err != nil {
		return "", err
	}
	return location.String(), nil
}

func (r *REST) Get(ctx kubeapi.Context, id string) (runtime.Object, error) {
	return nil, fmt.Errorf("BuildLog can't be retrieved")
}

func (r *REST) New() runtime.Object {
	return nil
}

func (r *REST) List(ctx kubeapi.Context, selector, fields labels.Selector) (runtime.Object, error) {
	return nil, fmt.Errorf("BuildLog can't be listed")
}

func (r *REST) Delete(ctx kubeapi.Context, id string) (<-chan runtime.Object, error) {
	return nil, fmt.Errorf("BuildLog can't be deleted")
}

func (r *REST) Create(ctx kubeapi.Context, obj runtime.Object) (<-chan runtime.Object, error) {
	return nil, fmt.Errorf("BuildLog can't be created")
}

func (r *REST) Update(ctx kubeapi.Context, obj runtime.Object) (<-chan runtime.Object, error) {
	return nil, fmt.Errorf("BuildLog can't be updated")
}
<|endoftext|>


<commit_before>package k8s

import (
	"context"
	"errors"
	"fmt"
	"sort"
	"strings"
	"sync"

	"github.com/blang/semver"
	core "k8s.io/api/core/v1"
	k8err "k8s.io/apimachinery/pkg/api/errors"
	meta "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/discovery"
	"k8s.io/client-go/kubernetes"

	"github.com/datawire/dlib/dlog"
	"github.com/telepresenceio/telepresence/rpc/v2/manager"
	"github.com/telepresenceio/telepresence/v2/pkg/client"
	"github.com/telepresenceio/telepresence/v2/pkg/k8sapi"
)

const supportedKubeAPIVersion = "1.17.0"

// Cluster is a Kubernetes cluster reference
type Cluster struct {
	*Config
	mappedNamespaces []string

	ingressInfo []*manager.IngressInfo

	// Main
	ki kubernetes.Interface

	// nsLock protects currentNamespaces and namespaceListener
	nsLock sync.Mutex

	// Current Namespace snapshot, get set by namespace watcher.
	currentNamespaces map[string]struct{}

	// Current Namespace snapshot, filtered by mappedNamespaces
	currentMappedNamespaces []string

	// Namespace listener. Notified when the currentNamespaces changes
	namespaceListener func(c context.Context)
}

func (kc *Cluster) ActualNamespace(namespace string) string {
	if namespace == "" {
		namespace = kc.Namespace
	}
	if !kc.namespaceExists(namespace) {
		namespace = ""
	}
	return namespace
}

// check uses a non-caching DiscoveryClientConfig to retrieve the server version
func (kc *Cluster) check(c context.Context) error {
	// The discover client is using context.TODO() so the timeout specified in our
	// context has no effect.
	errCh := make(chan error)
	go func() {
		dc, err := discovery.NewDiscoveryClientForConfig(kc.config)
		if err != nil {
			errCh <- err
			return
		}
		info, err := dc.ServerVersion()
		if err != nil {
			errCh <- err
			return
		}
		// Validate that the kubernetes server version is supported
		dlog.Infof(c, "Server version %s", info.GitVersion)
		gitVer, err := semver.Parse(strings.TrimPrefix(info.GitVersion, "v"))
		if err != nil {
			dlog.Errorf(c, "error converting version %s to semver: %s", info.GitVersion, err)
		}
		supGitVer, err := semver.Parse(supportedKubeAPIVersion)
		if err != nil {
			dlog.Errorf(c, "error converting known version %s to semver: %s", supportedKubeAPIVersion, err)
		}
		if gitVer.LT(supGitVer) {
			dlog.Errorf(c,
				"kubernetes server versions older than %s are not supported, using %s .",
				supportedKubeAPIVersion, info.GitVersion)
		}
		close(errCh)
	}()

	select {
	case <-c.Done():
	case err := <-errCh:
		if err == nil {
			return nil
		}
		if c.Err() == nil {
			return fmt.Errorf("initial cluster check failed: %w", client.RunError(err))
		}
	}
	return c.Err()
}

// Deployments returns all deployments found in the given Namespace
func (kc *Cluster) Deployments(c context.Context, namespace string) ([]k8sapi.Workload, error) {
	ds, err := kc.ki.AppsV1().Deployments(namespace).List(c, meta.ListOptions{})
	if err != nil {
		return nil, err
	}
	di := ds.Items
	objs := make([]k8sapi.Workload, len(di))
	for i := range di {
		objs[i] = k8sapi.Deployment(&di[i])
	}
	return objs, nil
}

// ReplicaSets returns all replica sets found in the given Namespace
func (kc *Cluster) ReplicaSets(c context.Context, namespace string) ([]k8sapi.Workload, error) {
	rs, err := kc.ki.AppsV1().ReplicaSets(namespace).List(c, meta.ListOptions{})
	if err != nil {
		return nil, err
	}
	ri := rs.Items
	objs := make([]k8sapi.Workload, len(ri))
	for i := range ri {
		objs[i] = k8sapi.ReplicaSet(&ri[i])
	}
	return objs, nil
}

// StatefulSets returns all stateful sets found in the given Namespace
func (kc *Cluster) StatefulSets(c context.Context, namespace string) ([]k8sapi.Workload, error) {
	ss, err := kc.ki.AppsV1().StatefulSets(namespace).List(c, meta.ListOptions{})
	if err != nil {
		return nil, err
	}
	si := ss.Items
	objs := make([]k8sapi.Workload, len(si))
	for i := range si {
		objs[i] = k8sapi.StatefulSet(&si[i])
	}
	return objs, nil
}

// Pods returns all pods found in the given Namespace
func (kc *Cluster) Pods(c context.Context, namespace string) ([]core.Pod, error) {
	ps, err := kc.ki.CoreV1().Pods(namespace).List(c, meta.ListOptions{})
	if err != nil {
		return nil, err
	}
	return ps.Items, nil
}

// FindPodFromSelector returns a pod with the given name-hex-hex
func (kc *Cluster) FindPodFromSelector(c context.Context, namespace string, selector map[string]string) (*core.Pod, error) {
	pods, err := kc.Pods(c, namespace)
	if err != nil {
		return nil, err
	}

	for i := range pods {
		podLabels := pods[i].GetLabels()
		match := true
		// check if selector is in labels
		for key, val := range selector {
			if podLabels[key] != val {
				match = false
				break
			}
		}
		if match {
			return &pods[i], nil
		}
	}

	return nil, errors.New("pod not found")
}

// FindWorkload returns a workload for the given name, namespace, and workloadKind. The workloadKind
// is optional. A search is performed in the following order if it is empty:
//
//   1. Deployments
//   2. ReplicaSets
//   3. StatefulSets
//
// The first match is returned.
func (kc *Cluster) FindWorkload(c context.Context, namespace, name, workloadKind string) (obj k8sapi.Workload, err error) {
	switch workloadKind {
	case "Deployment":
		obj, err = k8sapi.GetDeployment(c, name, namespace)
	case "ReplicaSet":
		obj, err = k8sapi.GetReplicaSet(c, name, namespace)
	case "StatefulSet":
		obj, err = k8sapi.GetStatefulSet(c, name, namespace)
	case "":
		for _, wk := range []string{"Deployment", "ReplicaSet", "StatefulSet"} {
			if obj, err = kc.FindWorkload(c, namespace, name, wk); err == nil {
				return obj, nil
			}
			if !k8err.IsNotFound(err) {
				return nil, err
			}
		}
		err = k8err.NewNotFound(core.Resource("workload"), name+"."+namespace)
	default:
		return nil, fmt.Errorf("unsupported workload kind: %q", workloadKind)
	}
	return obj, err
}

// FindSvc finds a service with the given name in the given Namespace and returns
// either a copy of that service or nil if no such service could be found.
func (kc *Cluster) FindSvc(c context.Context, namespace, name string) (*core.Service, error) {
	return kc.ki.CoreV1().Services(namespace).Get(c, name, meta.GetOptions{})
}

// findAllSvcByType finds services with the given service type in all namespaces of the cluster returns
// a slice containing a copy of those services.
func (kc *Cluster) findAllSvcByType(c context.Context, svcType core.ServiceType) ([]*core.Service, error) {
	// NOTE: This is expensive in terms of bandwidth on a large cluster. We currently only use this
	// to retrieve ingress info and that task could be moved to the traffic-manager instead.
	var typedSvcs []*core.Service
	findTyped := func(ns string) error {
		ss, err := kc.ki.CoreV1().Services(ns).List(c, meta.ListOptions{})
		if err != nil {
			return err
		}
		for i := range ss.Items {
			s := &ss.Items[i]
			if s.Spec.Type == svcType {
				typedSvcs = append(typedSvcs, s)
			}
		}
		return nil
	}

	kc.nsLock.Lock()
	var mns []string
	if len(kc.mappedNamespaces) > 0 {
		mns = make([]string, len(kc.mappedNamespaces))
		copy(mns, kc.mappedNamespaces)
	}
	kc.nsLock.Unlock()

	if len(mns) > 0 {
		for _, ns := range mns {
			if err := findTyped(ns); err != nil {
				return nil, err
			}
		}
	} else if err := findTyped(""); err != nil {
		return nil, err
	}
	return typedSvcs, nil
}

func (kc *Cluster) namespaceExists(namespace string) (exists bool) {
	kc.nsLock.Lock()
	for _, n := range kc.currentMappedNamespaces {
		if n == namespace {
			exists = true
			break
		}
	}
	kc.nsLock.Unlock()
	return exists
}

func NewCluster(c context.Context, kubeFlags *Config, namespaces []string) (*Cluster, error) {
	rs, err := kubeFlags.ConfigFlags.ToRESTConfig()
	if err != nil {
		return nil, err
	}
	cs, err := kubernetes.NewForConfig(rs)
	if err != nil {
		return nil, err
	}
	if len(namespaces) == 1 && namespaces[0] == "all" {
		namespaces = nil
	} else {
		sort.Strings(namespaces)
	}

	ret := &Cluster{
		Config:            kubeFlags,
		mappedNamespaces:  namespaces,
		ki:                cs,
		currentNamespaces: make(map[string]struct{}),
	}

	if err := ret.check(c); err != nil {
		return nil, err
	}

	dlog.Infof(c, "Context: %s", ret.Context)
	dlog.Infof(c, "Server: %s", ret.Server)

	ret.startNamespaceWatcher(c)
	return ret, nil
}

func (kc *Cluster) GetCurrentNamespaces() []string {
	kc.nsLock.Lock()
	nss := make([]string, len(kc.currentMappedNamespaces))
	copy(nss, kc.currentMappedNamespaces)
	kc.nsLock.Unlock()
	return nss
}

func (kc *Cluster) GetClusterId(ctx context.Context) string {
	clusterID, _ := k8sapi.GetClusterID(ctx)
	return clusterID
}

func (kc *Cluster) WithK8sInterface(c context.Context) context.Context {
	return k8sapi.WithK8sInterface(c, kc.ki)
}
<commit_msg>Don't recreate the kubernetes.Interface when running the version check<commit_after>package k8s

import (
	"context"
	"errors"
	"fmt"
	"sort"
	"strings"
	"sync"

	"github.com/blang/semver"
	core "k8s.io/api/core/v1"
	k8err "k8s.io/apimachinery/pkg/api/errors"
	meta "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"

	"github.com/datawire/dlib/dlog"
	"github.com/telepresenceio/telepresence/rpc/v2/manager"
	"github.com/telepresenceio/telepresence/v2/pkg/client"
	"github.com/telepresenceio/telepresence/v2/pkg/k8sapi"
)

const supportedKubeAPIVersion = "1.17.0"

// Cluster is a Kubernetes cluster reference
type Cluster struct {
	*Config
	mappedNamespaces []string

	ingressInfo []*manager.IngressInfo

	// Main
	ki kubernetes.Interface

	// nsLock protects currentNamespaces and namespaceListener
	nsLock sync.Mutex

	// Current Namespace snapshot, get set by namespace watcher.
	currentNamespaces map[string]struct{}

	// Current Namespace snapshot, filtered by mappedNamespaces
	currentMappedNamespaces []string

	// Namespace listener. Notified when the currentNamespaces changes
	namespaceListener func(c context.Context)
}

func (kc *Cluster) ActualNamespace(namespace string) string {
	if namespace == "" {
		namespace = kc.Namespace
	}
	if !kc.namespaceExists(namespace) {
		namespace = ""
	}
	return namespace
}

// check uses a non-caching DiscoveryClientConfig to retrieve the server version
func (kc *Cluster) check(c context.Context) error {
	// The discover client is using context.TODO() so the timeout specified in our
	// context has no effect.
	errCh := make(chan error)
	go func() {
		defer close(errCh)
		info, err := k8sapi.GetK8sInterface(c).Discovery().ServerVersion()
		if err != nil {
			errCh <- err
			return
		}
		// Validate that the kubernetes server version is supported
		dlog.Infof(c, "Server version %s", info.GitVersion)
		gitVer, err := semver.Parse(strings.TrimPrefix(info.GitVersion, "v"))
		if err != nil {
			dlog.Errorf(c, "error converting version %s to semver: %s", info.GitVersion, err)
		}
		supGitVer, err := semver.Parse(supportedKubeAPIVersion)
		if err != nil {
			dlog.Errorf(c, "error converting known version %s to semver: %s", supportedKubeAPIVersion, err)
		}
		if gitVer.LT(supGitVer) {
			dlog.Errorf(c,
				"kubernetes server versions older than %s are not supported, using %s .",
				supportedKubeAPIVersion, info.GitVersion)
		}
	}()

	select {
	case <-c.Done():
	case err := <-errCh:
		if err == nil {
			return nil
		}
		if c.Err() == nil {
			return fmt.Errorf("initial cluster check failed: %w", client.RunError(err))
		}
	}
	return c.Err()
}

// Deployments returns all deployments found in the given Namespace
func (kc *Cluster) Deployments(c context.Context, namespace string) ([]k8sapi.Workload, error) {
	ds, err := kc.ki.AppsV1().Deployments(namespace).List(c, meta.ListOptions{})
	if err != nil {
		return nil, err
	}
	di := ds.Items
	objs := make([]k8sapi.Workload, len(di))
	for i := range di {
		objs[i] = k8sapi.Deployment(&di[i])
	}
	return objs, nil
}

// ReplicaSets returns all replica sets found in the given Namespace
func (kc *Cluster) ReplicaSets(c context.Context, namespace string) ([]k8sapi.Workload, error) {
	rs, err := kc.ki.AppsV1().ReplicaSets(namespace).List(c, meta.ListOptions{})
	if err != nil {
		return nil, err
	}
	ri := rs.Items
	objs := make([]k8sapi.Workload, len(ri))
	for i := range ri {
		objs[i] = k8sapi.ReplicaSet(&ri[i])
	}
	return objs, nil
}

// StatefulSets returns all stateful sets found in the given Namespace
func (kc *Cluster) StatefulSets(c context.Context, namespace string) ([]k8sapi.Workload, error) {
	ss, err := kc.ki.AppsV1().StatefulSets(namespace).List(c, meta.ListOptions{})
	if err != nil {
		return nil, err
	}
	si := ss.Items
	objs := make([]k8sapi.Workload, len(si))
	for i := range si {
		objs[i] = k8sapi.StatefulSet(&si[i])
	}
	return objs, nil
}

// Pods returns all pods found in the given Namespace
func (kc *Cluster) Pods(c context.Context, namespace string) ([]core.Pod, error) {
	ps, err := kc.ki.CoreV1().Pods(namespace).List(c, meta.ListOptions{})
	if err != nil {
		return nil, err
	}
	return ps.Items, nil
}

// FindPodFromSelector returns a pod with the given name-hex-hex
func (kc *Cluster) FindPodFromSelector(c context.Context, namespace string, selector map[string]string) (*core.Pod, error) {
	pods, err := kc.Pods(c, namespace)
	if err != nil {
		return nil, err
	}

	for i := range pods {
		podLabels := pods[i].GetLabels()
		match := true
		// check if selector is in labels
		for key, val := range selector {
			if podLabels[key] != val {
				match = false
				break
			}
		}
		if match {
			return &pods[i], nil
		}
	}

	return nil, errors.New("pod not found")
}

// FindWorkload returns a workload for the given name, namespace, and workloadKind. The workloadKind
// is optional. A search is performed in the following order if it is empty:
//
//   1. Deployments
//   2. ReplicaSets
//   3. StatefulSets
//
// The first match is returned.
func (kc *Cluster) FindWorkload(c context.Context, namespace, name, workloadKind string) (obj k8sapi.Workload, err error) {
	switch workloadKind {
	case "Deployment":
		obj, err = k8sapi.GetDeployment(c, name, namespace)
	case "ReplicaSet":
		obj, err = k8sapi.GetReplicaSet(c, name, namespace)
	case "StatefulSet":
		obj, err = k8sapi.GetStatefulSet(c, name, namespace)
	case "":
		for _, wk := range []string{"Deployment", "ReplicaSet", "StatefulSet"} {
			if obj, err = kc.FindWorkload(c, namespace, name, wk); err == nil {
				return obj, nil
			}
			if !k8err.IsNotFound(err) {
				return nil, err
			}
		}
		err = k8err.NewNotFound(core.Resource("workload"), name+"."+namespace)
	default:
		return nil, fmt.Errorf("unsupported workload kind: %q", workloadKind)
	}
	return obj, err
}

// FindSvc finds a service with the given name in the given Namespace and returns
// either a copy of that service or nil if no such service could be found.
func (kc *Cluster) FindSvc(c context.Context, namespace, name string) (*core.Service, error) {
	return kc.ki.CoreV1().Services(namespace).Get(c, name, meta.GetOptions{})
}

// findAllSvcByType finds services with the given service type in all namespaces of the cluster returns
// a slice containing a copy of those services.
func (kc *Cluster) findAllSvcByType(c context.Context, svcType core.ServiceType) ([]*core.Service, error) {
	// NOTE: This is expensive in terms of bandwidth on a large cluster. We currently only use this
	// to retrieve ingress info and that task could be moved to the traffic-manager instead.
	var typedSvcs []*core.Service
	findTyped := func(ns string) error {
		ss, err := kc.ki.CoreV1().Services(ns).List(c, meta.ListOptions{})
		if err != nil {
			return err
		}
		for i := range ss.Items {
			s := &ss.Items[i]
			if s.Spec.Type == svcType {
				typedSvcs = append(typedSvcs, s)
			}
		}
		return nil
	}

	kc.nsLock.Lock()
	var mns []string
	if len(kc.mappedNamespaces) > 0 {
		mns = make([]string, len(kc.mappedNamespaces))
		copy(mns, kc.mappedNamespaces)
	}
	kc.nsLock.Unlock()

	if len(mns) > 0 {
		for _, ns := range mns {
			if err := findTyped(ns); err != nil {
				return nil, err
			}
		}
	} else if err := findTyped(""); err != nil {
		return nil, err
	}
	return typedSvcs, nil
}

func (kc *Cluster) namespaceExists(namespace string) (exists bool) {
	kc.nsLock.Lock()
	for _, n := range kc.currentMappedNamespaces {
		if n == namespace {
			exists = true
			break
		}
	}
	kc.nsLock.Unlock()
	return exists
}

func NewCluster(c context.Context, kubeFlags *Config, namespaces []string) (*Cluster, error) {
	rs, err := kubeFlags.ConfigFlags.ToRESTConfig()
	if err != nil {
		return nil, err
	}
	cs, err := kubernetes.NewForConfig(rs)
	if err != nil {
		return nil, err
	}
	c = k8sapi.WithK8sInterface(c, cs)

	if len(namespaces) == 1 && namespaces[0] == "all" {
		namespaces = nil
	} else {
		sort.Strings(namespaces)
	}

	ret := &Cluster{
		Config:            kubeFlags,
		mappedNamespaces:  namespaces,
		ki:                cs,
		currentNamespaces: make(map[string]struct{}),
	}

	if err := ret.check(c); err != nil {
		return nil, err
	}

	dlog.Infof(c, "Context: %s", ret.Context)
	dlog.Infof(c, "Server: %s", ret.Server)

	ret.startNamespaceWatcher(c)
	return ret, nil
}

func (kc *Cluster) GetCurrentNamespaces() []string {
	kc.nsLock.Lock()
	nss := make([]string, len(kc.currentMappedNamespaces))
	copy(nss, kc.currentMappedNamespaces)
	kc.nsLock.Unlock()
	return nss
}

func (kc *Cluster) GetClusterId(ctx context.Context) string {
	clusterID, _ := k8sapi.GetClusterID(ctx)
	return clusterID
}

func (kc *Cluster) WithK8sInterface(c context.Context) context.Context {
	return k8sapi.WithK8sInterface(c, kc.ki)
}
<|endoftext|>


<commit_before>package imageprocessing

type Quality struct {
	// AbsoluteQuality is in range [1,100].
	AbsoluteQuality int
}

const DefaultQuality = 85

var _ Operation = &Quality{}

func (o *Quality) Apply(ctx *OperationContext) error {
	// TODO(imageprocessing)
	return nil
}
<commit_msg>Apply Quality<commit_after>package imageprocessing

type Quality struct {
	// AbsoluteQuality is in range [1,100].
	AbsoluteQuality int
}

const DefaultQuality = 85

var _ Operation = &Quality{}

func (o *Quality) Apply(ctx *OperationContext) error {
	ctx.Quality = o.AbsoluteQuality
	return nil
}
<|endoftext|>


<commit_before>package jsonmessage

import (
	"bytes"
	"fmt"
	"strings"
	"testing"
	"time"

	"github.com/docker/docker/pkg/jsonlog"
	"github.com/docker/docker/pkg/term"
)

func TestError(t *testing.T) {
	je := JSONError{404, "Not found"}
	if je.Error() != "Not found" {
		t.Fatalf("Expected 'Not found' got '%s'", je.Error())
	}
}

func TestProgress(t *testing.T) {
	termsz, err := term.GetWinsize(0)
	if err != nil {
		// we can safely ignore the err here
		termsz = nil
	}
	jp := JSONProgress{}
	if jp.String() != "" {
		t.Fatalf("Expected empty string, got '%s'", jp.String())
	}

	expected := "     1 B"
	jp2 := JSONProgress{Current: 1}
	if jp2.String() != expected {
		t.Fatalf("Expected %q, got %q", expected, jp2.String())
	}

	expectedStart := "[==========>                                        ]     20 B/100 B"
	if termsz != nil && termsz.Width <= 110 {
		expectedStart = "    20 B/100 B"
	}
	jp3 := JSONProgress{Current: 20, Total: 100, Start: time.Now().Unix()}
	// Just look at the start of the string
	// (the remaining time is really hard to test -_-)
	if jp3.String()[:len(expectedStart)] != expectedStart {
		t.Fatalf("Expected to start with %q, got %q", expectedStart, jp3.String())
	}

	expected = "[=========================>                         ]     50 B/100 B"
	if termsz != nil && termsz.Width <= 110 {
		expected = "    50 B/100 B"
	}
	jp4 := JSONProgress{Current: 50, Total: 100}
	if jp4.String() != expected {
		t.Fatalf("Expected %q, got %q", expected, jp4.String())
	}

	// this number can't be negative gh#7136
	expected = "[==================================================>]     50 B"
	if termsz != nil && termsz.Width <= 110 {
		expected = "    50 B"
	}
	jp5 := JSONProgress{Current: 50, Total: 40}
	if jp5.String() != expected {
		t.Fatalf("Expected %q, got %q", expected, jp5.String())
	}
}

func TestJSONMessageDisplay(t *testing.T) {
	now := time.Now()
	messages := map[JSONMessage][]string{
		// Empty
		JSONMessage{}: {"\n", "\n"},
		// Status
		JSONMessage{
			Status: "status",
		}: {
			"status\n",
			"status\n",
		},
		// General
		JSONMessage{
			Time:   now.Unix(),
			ID:     "ID",
			From:   "From",
			Status: "status",
		}: {
			fmt.Sprintf("%v ID: (from From) status\n", time.Unix(now.Unix(), 0).Format(jsonlog.RFC3339NanoFixed)),
			fmt.Sprintf("%v ID: (from From) status\n", time.Unix(now.Unix(), 0).Format(jsonlog.RFC3339NanoFixed)),
		},
		// General, with nano precision time
		JSONMessage{
			TimeNano: now.UnixNano(),
			ID:       "ID",
			From:     "From",
			Status:   "status",
		}: {
			fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(jsonlog.RFC3339NanoFixed)),
			fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(jsonlog.RFC3339NanoFixed)),
		},
		// General, with both times Nano is preferred
		JSONMessage{
			Time:     now.Unix(),
			TimeNano: now.UnixNano(),
			ID:       "ID",
			From:     "From",
			Status:   "status",
		}: {
			fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(jsonlog.RFC3339NanoFixed)),
			fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(jsonlog.RFC3339NanoFixed)),
		},
		// Stream over status
		JSONMessage{
			Status: "status",
			Stream: "stream",
		}: {
			"stream",
			"stream",
		},
		// With progress message
		JSONMessage{
			Status:          "status",
			ProgressMessage: "progressMessage",
		}: {
			"status progressMessage",
			"status progressMessage",
		},
		// With progress, stream empty
		JSONMessage{
			Status:   "status",
			Stream:   "",
			Progress: &JSONProgress{Current: 1},
		}: {
			"",
			fmt.Sprintf("%c[2K\rstatus      1 B\r", 27),
		},
	}

	// The tests :)
	for jsonMessage, expectedMessages := range messages {
		// Without terminal
		data := bytes.NewBuffer([]byte{})
		if err := jsonMessage.Display(data, false); err != nil {
			t.Fatal(err)
		}
		if data.String() != expectedMessages[0] {
			t.Fatalf("Expected %q, got %q", expectedMessages[0], data.String())
		}
		// With terminal
		data = bytes.NewBuffer([]byte{})
		if err := jsonMessage.Display(data, true); err != nil {
			t.Fatal(err)
		}
		if data.String() != expectedMessages[1] {
			t.Fatalf("Expected %q, got %q", expectedMessages[1], data.String())
		}
	}
}

// Test JSONMessage with an Error. It will return an error with the text as error, not the meaning of the HTTP code.
func TestJSONMessageDisplayWithJSONError(t *testing.T) {
	data := bytes.NewBuffer([]byte{})
	jsonMessage := JSONMessage{Error: &JSONError{404, "Can't find it"}}

	err := jsonMessage.Display(data, true)
	if err == nil || err.Error() != "Can't find it" {
		t.Fatalf("Expected a JSONError 404, got %q", err)
	}

	jsonMessage = JSONMessage{Error: &JSONError{401, "Anything"}}
	err = jsonMessage.Display(data, true)
	if err == nil || err.Error() != "Authentication is required." {
		t.Fatalf("Expected an error \"Authentication is required.\", got %q", err)
	}
}

func TestDisplayJSONMessagesStreamInvalidJSON(t *testing.T) {
	var (
		inFd uintptr
	)
	data := bytes.NewBuffer([]byte{})
	reader := strings.NewReader("This is not a 'valid' JSON []")
	inFd, _ = term.GetFdInfo(reader)

	if err := DisplayJSONMessagesStream(reader, data, inFd, false, nil); err == nil && err.Error()[:17] != "invalid character" {
		t.Fatalf("Should have thrown an error (invalid character in ..), got %q", err)
	}
}

func TestDisplayJSONMessagesStream(t *testing.T) {
	var (
		inFd uintptr
	)

	messages := map[string][]string{
		// empty string
		"": {
			"",
			""},
		// Without progress & ID
		"{ \"status\": \"status\" }": {
			"status\n",
			"status\n",
		},
		// Without progress, with ID
		"{ \"id\": \"ID\",\"status\": \"status\" }": {
			"ID: status\n",
			fmt.Sprintf("ID: status\n"),
		},
		// With progress
		"{ \"id\": \"ID\", \"status\": \"status\", \"progress\": \"ProgressMessage\" }": {
			"ID: status ProgressMessage",
			fmt.Sprintf("\n%c[%dAID: status ProgressMessage%c[%dB", 27, 1, 27, 1),
		},
		// With progressDetail
		"{ \"id\": \"ID\", \"status\": \"status\", \"progressDetail\": { \"Current\": 1} }": {
			"", // progressbar is disabled in non-terminal
			fmt.Sprintf("\n%c[%dA%c[2K\rID: status      1 B\r%c[%dB", 27, 1, 27, 27, 1),
		},
	}
	for jsonMessage, expectedMessages := range messages {
		data := bytes.NewBuffer([]byte{})
		reader := strings.NewReader(jsonMessage)
		inFd, _ = term.GetFdInfo(reader)

		// Without terminal
		if err := DisplayJSONMessagesStream(reader, data, inFd, false, nil); err != nil {
			t.Fatal(err)
		}
		if data.String() != expectedMessages[0] {
			t.Fatalf("Expected an %q, got %q", expectedMessages[0], data.String())
		}

		// With terminal
		data = bytes.NewBuffer([]byte{})
		reader = strings.NewReader(jsonMessage)
		if err := DisplayJSONMessagesStream(reader, data, inFd, true, nil); err != nil {
			t.Fatal(err)
		}
		if data.String() != expectedMessages[1] {
			t.Fatalf("Expected an %q, got %q", expectedMessages[1], data.String())
		}
	}

}
<commit_msg>pkg/jsonmessage: Wrap test failure messages so control characters align<commit_after>package jsonmessage

import (
	"bytes"
	"fmt"
	"strings"
	"testing"
	"time"

	"github.com/docker/docker/pkg/jsonlog"
	"github.com/docker/docker/pkg/term"
)

func TestError(t *testing.T) {
	je := JSONError{404, "Not found"}
	if je.Error() != "Not found" {
		t.Fatalf("Expected 'Not found' got '%s'", je.Error())
	}
}

func TestProgress(t *testing.T) {
	termsz, err := term.GetWinsize(0)
	if err != nil {
		// we can safely ignore the err here
		termsz = nil
	}
	jp := JSONProgress{}
	if jp.String() != "" {
		t.Fatalf("Expected empty string, got '%s'", jp.String())
	}

	expected := "     1 B"
	jp2 := JSONProgress{Current: 1}
	if jp2.String() != expected {
		t.Fatalf("Expected %q, got %q", expected, jp2.String())
	}

	expectedStart := "[==========>                                        ]     20 B/100 B"
	if termsz != nil && termsz.Width <= 110 {
		expectedStart = "    20 B/100 B"
	}
	jp3 := JSONProgress{Current: 20, Total: 100, Start: time.Now().Unix()}
	// Just look at the start of the string
	// (the remaining time is really hard to test -_-)
	if jp3.String()[:len(expectedStart)] != expectedStart {
		t.Fatalf("Expected to start with %q, got %q", expectedStart, jp3.String())
	}

	expected = "[=========================>                         ]     50 B/100 B"
	if termsz != nil && termsz.Width <= 110 {
		expected = "    50 B/100 B"
	}
	jp4 := JSONProgress{Current: 50, Total: 100}
	if jp4.String() != expected {
		t.Fatalf("Expected %q, got %q", expected, jp4.String())
	}

	// this number can't be negative gh#7136
	expected = "[==================================================>]     50 B"
	if termsz != nil && termsz.Width <= 110 {
		expected = "    50 B"
	}
	jp5 := JSONProgress{Current: 50, Total: 40}
	if jp5.String() != expected {
		t.Fatalf("Expected %q, got %q", expected, jp5.String())
	}
}

func TestJSONMessageDisplay(t *testing.T) {
	now := time.Now()
	messages := map[JSONMessage][]string{
		// Empty
		JSONMessage{}: {"\n", "\n"},
		// Status
		JSONMessage{
			Status: "status",
		}: {
			"status\n",
			"status\n",
		},
		// General
		JSONMessage{
			Time:   now.Unix(),
			ID:     "ID",
			From:   "From",
			Status: "status",
		}: {
			fmt.Sprintf("%v ID: (from From) status\n", time.Unix(now.Unix(), 0).Format(jsonlog.RFC3339NanoFixed)),
			fmt.Sprintf("%v ID: (from From) status\n", time.Unix(now.Unix(), 0).Format(jsonlog.RFC3339NanoFixed)),
		},
		// General, with nano precision time
		JSONMessage{
			TimeNano: now.UnixNano(),
			ID:       "ID",
			From:     "From",
			Status:   "status",
		}: {
			fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(jsonlog.RFC3339NanoFixed)),
			fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(jsonlog.RFC3339NanoFixed)),
		},
		// General, with both times Nano is preferred
		JSONMessage{
			Time:     now.Unix(),
			TimeNano: now.UnixNano(),
			ID:       "ID",
			From:     "From",
			Status:   "status",
		}: {
			fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(jsonlog.RFC3339NanoFixed)),
			fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(jsonlog.RFC3339NanoFixed)),
		},
		// Stream over status
		JSONMessage{
			Status: "status",
			Stream: "stream",
		}: {
			"stream",
			"stream",
		},
		// With progress message
		JSONMessage{
			Status:          "status",
			ProgressMessage: "progressMessage",
		}: {
			"status progressMessage",
			"status progressMessage",
		},
		// With progress, stream empty
		JSONMessage{
			Status:   "status",
			Stream:   "",
			Progress: &JSONProgress{Current: 1},
		}: {
			"",
			fmt.Sprintf("%c[2K\rstatus      1 B\r", 27),
		},
	}

	// The tests :)
	for jsonMessage, expectedMessages := range messages {
		// Without terminal
		data := bytes.NewBuffer([]byte{})
		if err := jsonMessage.Display(data, false); err != nil {
			t.Fatal(err)
		}
		if data.String() != expectedMessages[0] {
			t.Fatalf("Expected %q,got %q", expectedMessages[0], data.String())
		}
		// With terminal
		data = bytes.NewBuffer([]byte{})
		if err := jsonMessage.Display(data, true); err != nil {
			t.Fatal(err)
		}
		if data.String() != expectedMessages[1] {
			t.Fatalf("\nExpected %q\n     got %q", expectedMessages[1], data.String())
		}
	}
}

// Test JSONMessage with an Error. It will return an error with the text as error, not the meaning of the HTTP code.
func TestJSONMessageDisplayWithJSONError(t *testing.T) {
	data := bytes.NewBuffer([]byte{})
	jsonMessage := JSONMessage{Error: &JSONError{404, "Can't find it"}}

	err := jsonMessage.Display(data, true)
	if err == nil || err.Error() != "Can't find it" {
		t.Fatalf("Expected a JSONError 404, got %q", err)
	}

	jsonMessage = JSONMessage{Error: &JSONError{401, "Anything"}}
	err = jsonMessage.Display(data, true)
	if err == nil || err.Error() != "Authentication is required." {
		t.Fatalf("Expected an error \"Authentication is required.\", got %q", err)
	}
}

func TestDisplayJSONMessagesStreamInvalidJSON(t *testing.T) {
	var (
		inFd uintptr
	)
	data := bytes.NewBuffer([]byte{})
	reader := strings.NewReader("This is not a 'valid' JSON []")
	inFd, _ = term.GetFdInfo(reader)

	if err := DisplayJSONMessagesStream(reader, data, inFd, false, nil); err == nil && err.Error()[:17] != "invalid character" {
		t.Fatalf("Should have thrown an error (invalid character in ..), got %q", err)
	}
}

func TestDisplayJSONMessagesStream(t *testing.T) {
	var (
		inFd uintptr
	)

	messages := map[string][]string{
		// empty string
		"": {
			"",
			""},
		// Without progress & ID
		"{ \"status\": \"status\" }": {
			"status\n",
			"status\n",
		},
		// Without progress, with ID
		"{ \"id\": \"ID\",\"status\": \"status\" }": {
			"ID: status\n",
			fmt.Sprintf("ID: status\n"),
		},
		// With progress
		"{ \"id\": \"ID\", \"status\": \"status\", \"progress\": \"ProgressMessage\" }": {
			"ID: status ProgressMessage",
			fmt.Sprintf("\n%c[%dAID: status ProgressMessage%c[%dB", 27, 1, 27, 1),
		},
		// With progressDetail
		"{ \"id\": \"ID\", \"status\": \"status\", \"progressDetail\": { \"Current\": 1} }": {
			"", // progressbar is disabled in non-terminal
			fmt.Sprintf("\n%c[%dA%c[2K\rID: status      1 B\r%c[%dB", 27, 1, 27, 27, 1),
		},
	}
	for jsonMessage, expectedMessages := range messages {
		data := bytes.NewBuffer([]byte{})
		reader := strings.NewReader(jsonMessage)
		inFd, _ = term.GetFdInfo(reader)

		// Without terminal
		if err := DisplayJSONMessagesStream(reader, data, inFd, false, nil); err != nil {
			t.Fatal(err)
		}
		if data.String() != expectedMessages[0] {
			t.Fatalf("Expected an %q, got %q", expectedMessages[0], data.String())
		}

		// With terminal
		data = bytes.NewBuffer([]byte{})
		reader = strings.NewReader(jsonMessage)
		if err := DisplayJSONMessagesStream(reader, data, inFd, true, nil); err != nil {
			t.Fatal(err)
		}
		if data.String() != expectedMessages[1] {
			t.Fatalf("\nExpected %q\n     got %q", expectedMessages[1], data.String())
		}
	}

}
<|endoftext|>


<commit_before>package sqlstore

import (
	"testing"
	"time"

	m "github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/setting"
	. "github.com/smartystreets/goconvey/convey"
)

func TestQuotaCommandsAndQueries(t *testing.T) {

	Convey("Testing Qutoa commands & queries", t, func() {
		InitTestDB(t)
		userId := int64(1)
		orgId := int64(0)

		setting.Quota = setting.QuotaSettings{
			Enabled: true,
			Org: &setting.OrgQuota{
				User:       5,
				Dashboard:  5,
				DataSource: 5,
				ApiKey:     5,
			},
			User: &setting.UserQuota{
				Org: 5,
			},
			Global: &setting.GlobalQuota{
				Org:        5,
				User:       5,
				Dashboard:  5,
				DataSource: 5,
				ApiKey:     5,
				Session:    5,
			},
		}

		// create a new org and add user_id 1 as admin.
		// we will then have an org with 1 user. and a user
		// with 1 org.
		userCmd := m.CreateOrgCommand{
			Name:   "TestOrg",
			UserId: 1,
		}

		err := CreateOrg(&userCmd)
		So(err, ShouldBeNil)
		orgId = userCmd.Result.Id

		Convey("Given saved org quota for users", func() {
			orgCmd := m.UpdateOrgQuotaCmd{
				OrgId:  orgId,
				Target: "org_user",
				Limit:  10,
			}
			err := UpdateOrgQuota(&orgCmd)
			So(err, ShouldBeNil)

			Convey("Should be able to get saved quota by org id and target", func() {
				query := m.GetOrgQuotaByTargetQuery{OrgId: orgId, Target: "org_user", Default: 1}
				err = GetOrgQuotaByTarget(&query)

				So(err, ShouldBeNil)
				So(query.Result.Limit, ShouldEqual, 10)
			})
			Convey("Should be able to get default quota by org id and target", func() {
				query := m.GetOrgQuotaByTargetQuery{OrgId: 123, Target: "org_user", Default: 11}
				err = GetOrgQuotaByTarget(&query)

				So(err, ShouldBeNil)
				So(query.Result.Limit, ShouldEqual, 11)
			})
			Convey("Should be able to get used org quota when rows exist", func() {
				query := m.GetOrgQuotaByTargetQuery{OrgId: orgId, Target: "org_user", Default: 11}
				err = GetOrgQuotaByTarget(&query)

				So(err, ShouldBeNil)
				So(query.Result.Used, ShouldEqual, 1)
			})
			Convey("Should be able to get used org quota when no rows exist", func() {
				query := m.GetOrgQuotaByTargetQuery{OrgId: 2, Target: "org_user", Default: 11}
				err = GetOrgQuotaByTarget(&query)

				So(err, ShouldBeNil)
				So(query.Result.Used, ShouldEqual, 0)
			})
			Convey("Should be able to quota list for org", func() {
				query := m.GetOrgQuotasQuery{OrgId: orgId}
				err = GetOrgQuotas(&query)

				So(err, ShouldBeNil)
				So(len(query.Result), ShouldEqual, 4)
				for _, res := range query.Result {
					limit := 5 //default quota limit
					used := 0
					if res.Target == "org_user" {
						limit = 10 //customized quota limit.
						used = 1
					}
					So(res.Limit, ShouldEqual, limit)
					So(res.Used, ShouldEqual, used)

				}
			})
		})
		Convey("Given saved user quota for org", func() {
			userQuotaCmd := m.UpdateUserQuotaCmd{
				UserId: userId,
				Target: "org_user",
				Limit:  10,
			}
			err := UpdateUserQuota(&userQuotaCmd)
			So(err, ShouldBeNil)

			Convey("Should be able to get saved quota by user id and target", func() {
				query := m.GetUserQuotaByTargetQuery{UserId: userId, Target: "org_user", Default: 1}
				err = GetUserQuotaByTarget(&query)

				So(err, ShouldBeNil)
				So(query.Result.Limit, ShouldEqual, 10)
			})
			Convey("Should be able to get default quota by user id and target", func() {
				query := m.GetUserQuotaByTargetQuery{UserId: 9, Target: "org_user", Default: 11}
				err = GetUserQuotaByTarget(&query)

				So(err, ShouldBeNil)
				So(query.Result.Limit, ShouldEqual, 11)
			})
			Convey("Should be able to get used user quota when rows exist", func() {
				query := m.GetUserQuotaByTargetQuery{UserId: userId, Target: "org_user", Default: 11}
				err = GetUserQuotaByTarget(&query)

				So(err, ShouldBeNil)
				So(query.Result.Used, ShouldEqual, 1)
			})
			Convey("Should be able to get used user quota when no rows exist", func() {
				query := m.GetUserQuotaByTargetQuery{UserId: 2, Target: "org_user", Default: 11}
				err = GetUserQuotaByTarget(&query)

				So(err, ShouldBeNil)
				So(query.Result.Used, ShouldEqual, 0)
			})
			Convey("Should be able to quota list for user", func() {
				query := m.GetUserQuotasQuery{UserId: userId}
				err = GetUserQuotas(&query)

				So(err, ShouldBeNil)
				So(len(query.Result), ShouldEqual, 1)
				So(query.Result[0].Limit, ShouldEqual, 10)
				So(query.Result[0].Used, ShouldEqual, 1)
			})
		})

		Convey("Should be able to global user quota", func() {
			query := m.GetGlobalQuotaByTargetQuery{Target: "user", Default: 5}
			err = GetGlobalQuotaByTarget(&query)
			So(err, ShouldBeNil)

			So(query.Result.Limit, ShouldEqual, 5)
			So(query.Result.Used, ShouldEqual, 0)
		})
		Convey("Should be able to global org quota", func() {
			query := m.GetGlobalQuotaByTargetQuery{Target: "org", Default: 5}
			err = GetGlobalQuotaByTarget(&query)
			So(err, ShouldBeNil)

			So(query.Result.Limit, ShouldEqual, 5)
			So(query.Result.Used, ShouldEqual, 1)
		})

		Convey("Should org quota updating is successful even if it called multiple time", func() {
			orgCmd := m.UpdateOrgQuotaCmd{
				OrgId:  orgId,
				Target: "org_user",
				Limit:  5,
			}
			err := UpdateOrgQuota(&orgCmd)
			So(err, ShouldBeNil)

			query := m.GetOrgQuotaByTargetQuery{OrgId: orgId, Target: "org_user", Default: 1}
			err = GetOrgQuotaByTarget(&query)
			So(err, ShouldBeNil)
			So(query.Result.Limit, ShouldEqual, 5)

			// XXX: resolution of `Updated` column is 1sec, so this makes delay
			time.Sleep(1 * time.Second)

			orgCmd = m.UpdateOrgQuotaCmd{
				OrgId:  orgId,
				Target: "org_user",
				Limit:  10,
			}
			err = UpdateOrgQuota(&orgCmd)
			So(err, ShouldBeNil)

			query = m.GetOrgQuotaByTargetQuery{OrgId: orgId, Target: "org_user", Default: 1}
			err = GetOrgQuotaByTarget(&query)
			So(err, ShouldBeNil)
			So(query.Result.Limit, ShouldEqual, 10)
		})

		Convey("Should user quota updating is successful even if it called multiple time", func() {
			userQuotaCmd := m.UpdateUserQuotaCmd{
				UserId: userId,
				Target: "org_user",
				Limit:  5,
			}
			err := UpdateUserQuota(&userQuotaCmd)
			So(err, ShouldBeNil)

			query := m.GetUserQuotaByTargetQuery{UserId: userId, Target: "org_user", Default: 1}
			err = GetUserQuotaByTarget(&query)
			So(err, ShouldBeNil)
			So(query.Result.Limit, ShouldEqual, 5)

			// XXX: resolution of `Updated` column is 1sec, so this makes delay
			time.Sleep(1 * time.Second)

			userQuotaCmd = m.UpdateUserQuotaCmd{
				UserId: userId,
				Target: "org_user",
				Limit:  10,
			}
			err = UpdateUserQuota(&userQuotaCmd)
			So(err, ShouldBeNil)

			query = m.GetUserQuotaByTargetQuery{UserId: userId, Target: "org_user", Default: 1}
			err = GetUserQuotaByTarget(&query)
			So(err, ShouldBeNil)
			So(query.Result.Limit, ShouldEqual, 10)
		})
	})
}
<commit_msg>Put issue number to test code<commit_after>package sqlstore

import (
	"testing"
	"time"

	m "github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/setting"
	. "github.com/smartystreets/goconvey/convey"
)

func TestQuotaCommandsAndQueries(t *testing.T) {

	Convey("Testing Qutoa commands & queries", t, func() {
		InitTestDB(t)
		userId := int64(1)
		orgId := int64(0)

		setting.Quota = setting.QuotaSettings{
			Enabled: true,
			Org: &setting.OrgQuota{
				User:       5,
				Dashboard:  5,
				DataSource: 5,
				ApiKey:     5,
			},
			User: &setting.UserQuota{
				Org: 5,
			},
			Global: &setting.GlobalQuota{
				Org:        5,
				User:       5,
				Dashboard:  5,
				DataSource: 5,
				ApiKey:     5,
				Session:    5,
			},
		}

		// create a new org and add user_id 1 as admin.
		// we will then have an org with 1 user. and a user
		// with 1 org.
		userCmd := m.CreateOrgCommand{
			Name:   "TestOrg",
			UserId: 1,
		}

		err := CreateOrg(&userCmd)
		So(err, ShouldBeNil)
		orgId = userCmd.Result.Id

		Convey("Given saved org quota for users", func() {
			orgCmd := m.UpdateOrgQuotaCmd{
				OrgId:  orgId,
				Target: "org_user",
				Limit:  10,
			}
			err := UpdateOrgQuota(&orgCmd)
			So(err, ShouldBeNil)

			Convey("Should be able to get saved quota by org id and target", func() {
				query := m.GetOrgQuotaByTargetQuery{OrgId: orgId, Target: "org_user", Default: 1}
				err = GetOrgQuotaByTarget(&query)

				So(err, ShouldBeNil)
				So(query.Result.Limit, ShouldEqual, 10)
			})
			Convey("Should be able to get default quota by org id and target", func() {
				query := m.GetOrgQuotaByTargetQuery{OrgId: 123, Target: "org_user", Default: 11}
				err = GetOrgQuotaByTarget(&query)

				So(err, ShouldBeNil)
				So(query.Result.Limit, ShouldEqual, 11)
			})
			Convey("Should be able to get used org quota when rows exist", func() {
				query := m.GetOrgQuotaByTargetQuery{OrgId: orgId, Target: "org_user", Default: 11}
				err = GetOrgQuotaByTarget(&query)

				So(err, ShouldBeNil)
				So(query.Result.Used, ShouldEqual, 1)
			})
			Convey("Should be able to get used org quota when no rows exist", func() {
				query := m.GetOrgQuotaByTargetQuery{OrgId: 2, Target: "org_user", Default: 11}
				err = GetOrgQuotaByTarget(&query)

				So(err, ShouldBeNil)
				So(query.Result.Used, ShouldEqual, 0)
			})
			Convey("Should be able to quota list for org", func() {
				query := m.GetOrgQuotasQuery{OrgId: orgId}
				err = GetOrgQuotas(&query)

				So(err, ShouldBeNil)
				So(len(query.Result), ShouldEqual, 4)
				for _, res := range query.Result {
					limit := 5 //default quota limit
					used := 0
					if res.Target == "org_user" {
						limit = 10 //customized quota limit.
						used = 1
					}
					So(res.Limit, ShouldEqual, limit)
					So(res.Used, ShouldEqual, used)

				}
			})
		})
		Convey("Given saved user quota for org", func() {
			userQuotaCmd := m.UpdateUserQuotaCmd{
				UserId: userId,
				Target: "org_user",
				Limit:  10,
			}
			err := UpdateUserQuota(&userQuotaCmd)
			So(err, ShouldBeNil)

			Convey("Should be able to get saved quota by user id and target", func() {
				query := m.GetUserQuotaByTargetQuery{UserId: userId, Target: "org_user", Default: 1}
				err = GetUserQuotaByTarget(&query)

				So(err, ShouldBeNil)
				So(query.Result.Limit, ShouldEqual, 10)
			})
			Convey("Should be able to get default quota by user id and target", func() {
				query := m.GetUserQuotaByTargetQuery{UserId: 9, Target: "org_user", Default: 11}
				err = GetUserQuotaByTarget(&query)

				So(err, ShouldBeNil)
				So(query.Result.Limit, ShouldEqual, 11)
			})
			Convey("Should be able to get used user quota when rows exist", func() {
				query := m.GetUserQuotaByTargetQuery{UserId: userId, Target: "org_user", Default: 11}
				err = GetUserQuotaByTarget(&query)

				So(err, ShouldBeNil)
				So(query.Result.Used, ShouldEqual, 1)
			})
			Convey("Should be able to get used user quota when no rows exist", func() {
				query := m.GetUserQuotaByTargetQuery{UserId: 2, Target: "org_user", Default: 11}
				err = GetUserQuotaByTarget(&query)

				So(err, ShouldBeNil)
				So(query.Result.Used, ShouldEqual, 0)
			})
			Convey("Should be able to quota list for user", func() {
				query := m.GetUserQuotasQuery{UserId: userId}
				err = GetUserQuotas(&query)

				So(err, ShouldBeNil)
				So(len(query.Result), ShouldEqual, 1)
				So(query.Result[0].Limit, ShouldEqual, 10)
				So(query.Result[0].Used, ShouldEqual, 1)
			})
		})

		Convey("Should be able to global user quota", func() {
			query := m.GetGlobalQuotaByTargetQuery{Target: "user", Default: 5}
			err = GetGlobalQuotaByTarget(&query)
			So(err, ShouldBeNil)

			So(query.Result.Limit, ShouldEqual, 5)
			So(query.Result.Used, ShouldEqual, 0)
		})
		Convey("Should be able to global org quota", func() {
			query := m.GetGlobalQuotaByTargetQuery{Target: "org", Default: 5}
			err = GetGlobalQuotaByTarget(&query)
			So(err, ShouldBeNil)

			So(query.Result.Limit, ShouldEqual, 5)
			So(query.Result.Used, ShouldEqual, 1)
		})

		// related: https://github.com/grafana/grafana/issues/14342
		Convey("Should org quota updating is successful even if it called multiple time", func() {
			orgCmd := m.UpdateOrgQuotaCmd{
				OrgId:  orgId,
				Target: "org_user",
				Limit:  5,
			}
			err := UpdateOrgQuota(&orgCmd)
			So(err, ShouldBeNil)

			query := m.GetOrgQuotaByTargetQuery{OrgId: orgId, Target: "org_user", Default: 1}
			err = GetOrgQuotaByTarget(&query)
			So(err, ShouldBeNil)
			So(query.Result.Limit, ShouldEqual, 5)

			// XXX: resolution of `Updated` column is 1sec, so this makes delay
			time.Sleep(1 * time.Second)

			orgCmd = m.UpdateOrgQuotaCmd{
				OrgId:  orgId,
				Target: "org_user",
				Limit:  10,
			}
			err = UpdateOrgQuota(&orgCmd)
			So(err, ShouldBeNil)

			query = m.GetOrgQuotaByTargetQuery{OrgId: orgId, Target: "org_user", Default: 1}
			err = GetOrgQuotaByTarget(&query)
			So(err, ShouldBeNil)
			So(query.Result.Limit, ShouldEqual, 10)
		})

		// related: https://github.com/grafana/grafana/issues/14342
		Convey("Should user quota updating is successful even if it called multiple time", func() {
			userQuotaCmd := m.UpdateUserQuotaCmd{
				UserId: userId,
				Target: "org_user",
				Limit:  5,
			}
			err := UpdateUserQuota(&userQuotaCmd)
			So(err, ShouldBeNil)

			query := m.GetUserQuotaByTargetQuery{UserId: userId, Target: "org_user", Default: 1}
			err = GetUserQuotaByTarget(&query)
			So(err, ShouldBeNil)
			So(query.Result.Limit, ShouldEqual, 5)

			// XXX: resolution of `Updated` column is 1sec, so this makes delay
			time.Sleep(1 * time.Second)

			userQuotaCmd = m.UpdateUserQuotaCmd{
				UserId: userId,
				Target: "org_user",
				Limit:  10,
			}
			err = UpdateUserQuota(&userQuotaCmd)
			So(err, ShouldBeNil)

			query = m.GetUserQuotaByTargetQuery{UserId: userId, Target: "org_user", Default: 1}
			err = GetUserQuotaByTarget(&query)
			So(err, ShouldBeNil)
			So(query.Result.Limit, ShouldEqual, 10)
		})
	})
}
<|endoftext|>


<commit_before>package application

import (
	"fmt"
	"github.com/stretchr/testify/assert"
	"github.com/thoas/imaging"
	"github.com/thoas/picfit/dummy"
	"github.com/thoas/picfit/engines"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os"
	"path"
	"testing"
)

type Dimension struct {
	Width  int
	Height int
}

type TestRequest struct {
	URL        string
	Dimensions *Dimension
}

func newDummyApplication() *Application {
	app := NewApplication()
	app.SourceStorage = &dummy.DummyStorage{}
	app.DestStorage = &dummy.DummyStorage{}
	app.KVStore = &dummy.DummyKVStore{}
	app.Engine = &engines.GoImageEngine{}

	return app
}

func newHTTPServer() *httptest.Server {
	return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

		if r.Method == "GET" {
			f, err := os.Open(path.Join("testdata", r.URL.Path))
			defer f.Close()

			if err != nil {
				w.WriteHeader(500)
			} else {
				bytes, _ := ioutil.ReadAll(f)

				w.Header().Set("Content-Length", fmt.Sprintf("%d\n\n%v", len(bytes), bytes))
				w.Write(bytes)
				w.WriteHeader(200)
			}
		}
	}))
}

func TestDummyApplication(t *testing.T) {
	ts := newHTTPServer()
	defer ts.Close()

	app := newDummyApplication()

	filenames := []string{"avatar.png", "schwarzy.jpg", "giphy.gif"}

	for _, filename := range filenames {
		u, _ := url.Parse(ts.URL + "/" + filename)

		tests := []*TestRequest{
			&TestRequest{
				URL: fmt.Sprintf("http://example.com/display?url=%s&w=50&h=50&op=resize", u.String()),
				Dimensions: &Dimension{
					Width:  50,
					Height: 50,
				},
			},
			&TestRequest{
				URL: fmt.Sprintf("http://example.com/display?url=%s&w=100&h=30&op=resize", u.String()),
				Dimensions: &Dimension{
					Width:  100,
					Height: 30,
				},
			},
			&TestRequest{
				URL: fmt.Sprintf("http://example.com/display?url=%s&w=50&h=50&op=thumbnail", u.String()),
				Dimensions: &Dimension{
					Width:  50,
					Height: 50,
				},
			},
		}

		for _, test := range tests {
			request, _ := http.NewRequest("GET", test.URL, nil)

			res := httptest.NewRecorder()

			handler := app.ServeHTTP(ImageHandler)

			handler.ServeHTTP(res, request)

			img, err := imaging.Decode(res.Body)

			assert.Nil(t, err)

			if img.Bounds().Max.X != test.Dimensions.Width {
				t.Fatalf("Invalid width for %s: %d != %d", filename, img.Bounds().Max.X, test.Dimensions.Width)
			}

			if img.Bounds().Max.Y != test.Dimensions.Height {
				t.Fatalf("Invalid width for %s: %d != %d", filename, img.Bounds().Max.Y, test.Dimensions.Height)
			}
		}
	}

}
<commit_msg>Add Content-Type header<commit_after>package application

import (
	"fmt"
	"github.com/stretchr/testify/assert"
	"github.com/thoas/imaging"
	"github.com/thoas/picfit/dummy"
	"github.com/thoas/picfit/engines"
	"io/ioutil"
	"mime"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os"
	"path"
	"testing"
)

type Dimension struct {
	Width  int
	Height int
}

type TestRequest struct {
	URL        string
	Dimensions *Dimension
}

var filenames = []string{"avatar.png", "schwarzy.jpg", "giphy.gif"}

func newDummyApplication() *Application {
	app := NewApplication()
	app.SourceStorage = &dummy.DummyStorage{}
	app.DestStorage = &dummy.DummyStorage{}
	app.KVStore = &dummy.DummyKVStore{}
	app.Engine = &engines.GoImageEngine{}

	return app
}

func newHTTPServer() *httptest.Server {
	return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

		if r.Method == "GET" {
			f, err := os.Open(path.Join("testdata", r.URL.Path))
			defer f.Close()

			if err != nil {
				w.WriteHeader(500)
			} else {
				bytes, _ := ioutil.ReadAll(f)

				contentType := mime.TypeByExtension(path.Ext(r.URL.Path))

				w.Header().Add("Content-Type", contentType)
				w.Write(bytes)
				w.WriteHeader(200)
			}
		}
	}))
}

func TestDummyApplication(t *testing.T) {
	ts := newHTTPServer()
	defer ts.Close()

	app := newDummyApplication()

	for _, filename := range filenames {
		u, _ := url.Parse(ts.URL + "/" + filename)

		tests := []*TestRequest{
			&TestRequest{
				URL: fmt.Sprintf("http://example.com/display?url=%s&w=50&h=50&op=resize", u.String()),
				Dimensions: &Dimension{
					Width:  50,
					Height: 50,
				},
			},
			&TestRequest{
				URL: fmt.Sprintf("http://example.com/display?url=%s&w=100&h=30&op=resize", u.String()),
				Dimensions: &Dimension{
					Width:  100,
					Height: 30,
				},
			},
			&TestRequest{
				URL: fmt.Sprintf("http://example.com/display?url=%s&w=50&h=50&op=thumbnail", u.String()),
				Dimensions: &Dimension{
					Width:  50,
					Height: 50,
				},
			},
		}

		for _, test := range tests {
			request, _ := http.NewRequest("GET", test.URL, nil)

			res := httptest.NewRecorder()

			handler := app.ServeHTTP(ImageHandler)

			handler.ServeHTTP(res, request)

			img, err := imaging.Decode(res.Body)

			assert.Nil(t, err)

			if img.Bounds().Max.X != test.Dimensions.Width {
				t.Fatalf("Invalid width for %s: %d != %d", filename, img.Bounds().Max.X, test.Dimensions.Width)
			}

			if img.Bounds().Max.Y != test.Dimensions.Height {
				t.Fatalf("Invalid width for %s: %d != %d", filename, img.Bounds().Max.Y, test.Dimensions.Height)
			}
		}
	}

}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Skaffold 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 acr

import (
	"bufio"
	"context"
	"io"
	"net/http"
	"strings"
	"time"

	cr "github.com/Azure/azure-sdk-for-go/services/containerregistry/mgmt/2018-09-01/containerregistry"
	"github.com/Azure/go-autorest/autorest/azure/auth"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/tag"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
	"github.com/pkg/errors"
)

const BuildStatusHeader = "x-ms-meta-Complete"

func (b *Builder) Build(ctx context.Context, out io.Writer, tagger tag.Tagger, artifacts []*latest.Artifact) ([]build.Artifact, error) {
	return build.InParallel(ctx, out, tagger, artifacts, b.buildArtifact)
}

func (b *Builder) buildArtifact(ctx context.Context, out io.Writer, tagger tag.Tagger, artifact *latest.Artifact) (string, error) {
	client := cr.NewRegistriesClient(b.Credentials.SubscriptionID)
	authorizer, err := auth.NewClientCredentialsConfig(b.Credentials.ClientID, b.Credentials.ClientSecret, b.Credentials.TenantID).Authorizer()
	if err != nil {
		return "", errors.Wrap(err, "authorizing client")
	}
	client.Authorizer = authorizer

	imageTag, err := tagger.GenerateFullyQualifiedImageName(artifact.Workspace, &tag.Options{
		Digest:    util.RandomID(),
		ImageName: artifact.ImageName,
	})
	if err != nil {
		return "", errors.Wrap(err, "create fully qualified image name")
	}
	registryName := getRegistryName(imageTag)

	result, err := client.GetBuildSourceUploadURL(ctx, b.ResourceGroup, registryName)
	if err != nil {
		return "", errors.Wrap(err, "build source upload url")
	}
	blob := NewBlobStorage(*result.UploadURL)

	err = docker.CreateDockerTarGzContext(blob.Buffer, artifact.Workspace, artifact.DockerArtifact)
	if err != nil {
		return "", errors.Wrap(err, "create context tar.gz")
	}

	err = blob.UploadFileToBlob()
	if err != nil {
		return "", errors.Wrap(err, "upload file to blob")
	}

	//acr needs the image tag formatted as <repository>:<tag>
	imageTag = getImageTagWithoutFQDN(imageTag)

	buildRequest := cr.DockerBuildRequest{
		ImageNames:     &[]string{imageTag},
		IsPushEnabled:  util.BoolPtr(true),
		SourceLocation: result.RelativePath,
		Platform: &cr.PlatformProperties{
			Variant:      cr.V8,
			Os:           cr.Linux,
			Architecture: cr.Amd64,
		},
		DockerFilePath: &artifact.DockerArtifact.DockerfilePath,
		Type:           cr.TypeDockerBuildRequest,
	}
	future, err := client.ScheduleRun(ctx, b.ResourceGroup, registryName, buildRequest)
	if err != nil {
		return "", errors.Wrap(err, "schedule build request")
	}

	run, err := future.Result(client)
	if err != nil {
		return "", errors.Wrap(err, "get run id")
	}
	runID := *run.RunID

	runsClient := cr.NewRunsClient(b.Credentials.SubscriptionID)
	runsClient.Authorizer = client.Authorizer
	logURL, err := runsClient.GetLogSasURL(ctx, b.ResourceGroup, registryName, runID)
	if err != nil {
		return "", errors.Wrap(err, "get log url")
	}

	err = streamBuildLogs(*logURL.LogLink, out)
	if err != nil {
		return "", errors.Wrap(err, "polling build status")
	}

	return imageTag, nil
}

func streamBuildLogs(logURL string, out io.Writer) error {
	offset := int32(0)
	for {
		resp, err := http.Get(logURL)
		if err != nil {
			return err
		}

		if resp.StatusCode == http.StatusNotFound {
			//if blob is not available yet, try again
			time.Sleep(2 * time.Second)
			continue
		}

		scanner := bufio.NewScanner(resp.Body)
		line := int32(0)
		for scanner.Scan() {
			if line >= offset {
				out.Write(scanner.Bytes())
				out.Write([]byte("\n"))
				offset++
			}
			line++
		}
		resp.Body.Close()

		if offset > 0 {
			switch resp.Header.Get(BuildStatusHeader) {
			case "":
				continue
			case "internalerror":
			case "failed":
				return errors.New("run failed")
			case "timedout":
				return errors.New("run timed out")
			case "canceled":
				return errors.New("run was canceled")
			default:
				return nil
			}
		}

		time.Sleep(2 * time.Second)
	}
}

func getImageTagWithoutFQDN(imageTag string) string {
	return imageTag[strings.Index(imageTag, "/")+1:]
}

//acr URL is <registryname>.azurecr.io
func getRegistryName(imageTag string) string {
	return imageTag[:strings.Index(imageTag, ".")]
}
<commit_msg>Synced upstream<commit_after>/*
Copyright 2018 The Skaffold 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 acr

import (
	"bufio"
	"context"
	"io"
	"net/http"
	"strings"
	"time"

	cr "github.com/Azure/azure-sdk-for-go/services/containerregistry/mgmt/2018-09-01/containerregistry"
	"github.com/Azure/go-autorest/autorest/azure/auth"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/tag"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
	"github.com/pkg/errors"
)

const BuildStatusHeader = "x-ms-meta-Complete"

func (b *Builder) Build(ctx context.Context, out io.Writer, tagger tag.Tagger, artifacts []*latest.Artifact) ([]build.Artifact, error) {
	return build.InParallel(ctx, out, tagger, artifacts, b.buildArtifact)
}

func (b *Builder) buildArtifact(ctx context.Context, out io.Writer, tagger tag.Tagger, artifact *latest.Artifact) (string, error) {
	client := cr.NewRegistriesClient(b.Credentials.SubscriptionID)
	authorizer, err := auth.NewClientCredentialsConfig(b.Credentials.ClientID, b.Credentials.ClientSecret, b.Credentials.TenantID).Authorizer()
	if err != nil {
		return "", errors.Wrap(err, "authorizing client")
	}
	client.Authorizer = authorizer

	imageTag, err := tagger.GenerateFullyQualifiedImageName(artifact.Workspace, &tag.Options{
		Digest:    util.RandomID(),
		ImageName: artifact.ImageName,
	})
	if err != nil {
		return "", errors.Wrap(err, "create fully qualified image name")
	}
	registryName := getRegistryName(imageTag)

	result, err := client.GetBuildSourceUploadURL(ctx, b.ResourceGroup, registryName)
	if err != nil {
		return "", errors.Wrap(err, "build source upload url")
	}
	blob := NewBlobStorage(*result.UploadURL)

	err = docker.CreateDockerTarGzContext(ctx, blob.Buffer, artifact.Workspace, artifact.DockerArtifact)
	if err != nil {
		return "", errors.Wrap(err, "create context tar.gz")
	}

	err = blob.UploadFileToBlob()
	if err != nil {
		return "", errors.Wrap(err, "upload file to blob")
	}

	//acr needs the image tag formatted as <repository>:<tag>
	imageTag = getImageTagWithoutFQDN(imageTag)

	buildRequest := cr.DockerBuildRequest{
		ImageNames:     &[]string{imageTag},
		IsPushEnabled:  util.BoolPtr(true),
		SourceLocation: result.RelativePath,
		Platform: &cr.PlatformProperties{
			Variant:      cr.V8,
			Os:           cr.Linux,
			Architecture: cr.Amd64,
		},
		DockerFilePath: &artifact.DockerArtifact.DockerfilePath,
		Type:           cr.TypeDockerBuildRequest,
	}
	future, err := client.ScheduleRun(ctx, b.ResourceGroup, registryName, buildRequest)
	if err != nil {
		return "", errors.Wrap(err, "schedule build request")
	}

	run, err := future.Result(client)
	if err != nil {
		return "", errors.Wrap(err, "get run id")
	}
	runID := *run.RunID

	runsClient := cr.NewRunsClient(b.Credentials.SubscriptionID)
	runsClient.Authorizer = client.Authorizer
	logURL, err := runsClient.GetLogSasURL(ctx, b.ResourceGroup, registryName, runID)
	if err != nil {
		return "", errors.Wrap(err, "get log url")
	}

	err = streamBuildLogs(*logURL.LogLink, out)
	if err != nil {
		return "", errors.Wrap(err, "polling build status")
	}

	return imageTag, nil
}

func streamBuildLogs(logURL string, out io.Writer) error {
	offset := int32(0)
	for {
		resp, err := http.Get(logURL)
		if err != nil {
			return err
		}

		if resp.StatusCode == http.StatusNotFound {
			//if blob is not available yet, try again
			time.Sleep(2 * time.Second)
			continue
		}

		scanner := bufio.NewScanner(resp.Body)
		line := int32(0)
		for scanner.Scan() {
			if line >= offset {
				out.Write(scanner.Bytes())
				out.Write([]byte("\n"))
				offset++
			}
			line++
		}
		resp.Body.Close()

		if offset > 0 {
			switch resp.Header.Get(BuildStatusHeader) {
			case "":
				continue
			case "internalerror":
			case "failed":
				return errors.New("run failed")
			case "timedout":
				return errors.New("run timed out")
			case "canceled":
				return errors.New("run was canceled")
			default:
				return nil
			}
		}

		time.Sleep(2 * time.Second)
	}
}

func getImageTagWithoutFQDN(imageTag string) string {
	return imageTag[strings.Index(imageTag, "/")+1:]
}

//acr URL is <registryname>.azurecr.io
func getRegistryName(imageTag string) string {
	return imageTag[:strings.Index(imageTag, ".")]
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"os"
	"time"

	log "github.com/sirupsen/logrus"

	"context"

	"github.com/networld-to/homecontrol/hue"
	"github.com/networld-to/homecontrol/version"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

var (
	address    = flag.String("host", "127.0.0.1:50051", "The gRPC service endpoint that will be contacted.")
	cmd        = flag.String("cmd", "groups", "The command that will be executed.")
	group      = flag.Int("group", 2, "The light group ID.")
	tls        = flag.Bool("tls", false, "Activate TLS communication channel encryption.")
	brightness = flag.Float64("brightness", 0.66, "Light brightness in percentage. Value between 0 and 1.")
)

func must(err error) {
	if err != nil {
		log.Fatalln(err)
	}
}

func getGroups(client hue.LightsClient) *hue.Groups {
	opts := getCallOptions()
	resp, err := client.GetGroups(context.Background(), &hue.LightsRequest{}, opts...)
	must(err)
	return resp
}

func getSensors(client hue.LightsClient) *hue.Sensors {
	opts := getCallOptions()
	resp, err := client.GetSensors(context.Background(), &hue.SensorRequest{}, opts...)
	must(err)
	return resp
}

func switchOn(client hue.LightsClient, group int) {
	opts := getCallOptions()
	_, err := client.SwitchOn(context.Background(), &hue.LightsRequest{Group: int32(group), BrightnessPercent: float32(*brightness)}, opts...)
	must(err)
}

func switchOff(client hue.LightsClient, group int) {
	opts := getCallOptions()
	_, err := client.SwitchOff(context.Background(), &hue.LightsRequest{Group: int32(group)}, opts...)
	must(err)
}

func blink(client hue.LightsClient, group int) {
	opts := getCallOptions()
	_, err := client.Blink(context.Background(), &hue.LightsRequest{Group: int32(group), BrightnessPercent: float32(*brightness)}, opts...)
	must(err)
}

func getCallOptions() []grpc.CallOption {
	opts := []grpc.CallOption{
		grpc.FailFast(true),
		grpc.MaxCallSendMsgSize(1024),
		grpc.MaxCallRecvMsgSize(5120),
	}
	return opts
}

func getDialOptions() []grpc.DialOption {
	opts := []grpc.DialOption{
		// grpc.WithInsecure(),
		grpc.WithTimeout(10 * time.Second),
		grpc.WithBlock(),
		grpc.WithBackoffMaxDelay(1 * time.Second),
	}
	return opts
}

func main() {
	flag.Parse()

	start := time.Now()
	opts := getDialOptions()
	if *tls {
		cred, err := credentials.NewClientTLSFromFile(os.Getenv("HOME")+"/.homecontrol/server.crt", "Homecontrol")
		info := cred.Info()
		log.WithField("tls", true).
			WithField("tls_version", info.ProtocolVersion).
			WithField("tls_server_name", info.ServerName).
			Info("TLS Enabled")
		if err != nil {
			log.Fatalf("failed TLS: %v", err)
		}
		opts = append(opts, grpc.WithTransportCredentials(cred))
	} else {
		log.Warn("Insecure connection. No TLS")
		opts = append(opts, grpc.WithInsecure())
	}
	conn, err := grpc.Dial(*address, opts...)
	must(err)
	defer conn.Close()

	hue := hue.NewLightsClient(conn)
	ver := version.NewVersionClient(conn)

	log.WithField("address", *address).WithField("cmd", *cmd).WithField("group", *group).Info("Executing command")
	switch *cmd {
	case "on":
		switchOn(hue, *group)
	case "off":
		switchOff(hue, *group)
	case "blink":
		blink(hue, *group)
	case "sensors":
		resp := getSensors(hue)
		for _, sensor := range resp.Sensors {
			log.Printf("Hue Light Sensor: %v", sensor.String())
		}
	case "version":
		log.WithField("version", version.Version).WithField("build", version.Build).Info("Client Version")
		resp, err := ver.Version(context.Background(), &version.VersionMessage{Version: version.Version, Build: version.Build})
		must(err)
		log.WithField("version", resp.Version).WithField("build", resp.Build).Info("Server Version")
	default:
		resp := getGroups(hue)
		log.Printf("Hue Light Groups: %v", resp.Groups)
	}

	end := time.Now()
	log.WithField("exec_time", end.Sub(start)).Printf("Execution finished")
}
<commit_msg>Improving sensors output (client-side)<commit_after>package main

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

	log "github.com/sirupsen/logrus"

	"context"

	"github.com/networld-to/homecontrol/hue"
	"github.com/networld-to/homecontrol/version"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

var (
	address    = flag.String("host", "127.0.0.1:50051", "The gRPC service endpoint that will be contacted.")
	cmd        = flag.String("cmd", "groups", "The command that will be executed.")
	group      = flag.Int("group", 2, "The light group ID.")
	tls        = flag.Bool("tls", false, "Activate TLS communication channel encryption.")
	brightness = flag.Float64("brightness", 0.66, "Light brightness in percentage. Value between 0 and 1.")
)

func must(err error) {
	if err != nil {
		log.Fatalln(err)
	}
}

func getGroups(client hue.LightsClient) *hue.Groups {
	opts := getCallOptions()
	resp, err := client.GetGroups(context.Background(), &hue.LightsRequest{}, opts...)
	must(err)
	return resp
}

func getSensors(client hue.LightsClient) *hue.Sensors {
	opts := getCallOptions()
	resp, err := client.GetSensors(context.Background(), &hue.SensorRequest{}, opts...)
	must(err)
	return resp
}

func switchOn(client hue.LightsClient, group int) {
	opts := getCallOptions()
	_, err := client.SwitchOn(context.Background(), &hue.LightsRequest{Group: int32(group), BrightnessPercent: float32(*brightness)}, opts...)
	must(err)
}

func switchOff(client hue.LightsClient, group int) {
	opts := getCallOptions()
	_, err := client.SwitchOff(context.Background(), &hue.LightsRequest{Group: int32(group)}, opts...)
	must(err)
}

func blink(client hue.LightsClient, group int) {
	opts := getCallOptions()
	_, err := client.Blink(context.Background(), &hue.LightsRequest{Group: int32(group), BrightnessPercent: float32(*brightness)}, opts...)
	must(err)
}

func getCallOptions() []grpc.CallOption {
	opts := []grpc.CallOption{
		grpc.FailFast(true),
		grpc.MaxCallSendMsgSize(1024),
		grpc.MaxCallRecvMsgSize(5120),
	}
	return opts
}

func getDialOptions() []grpc.DialOption {
	opts := []grpc.DialOption{
		// grpc.WithInsecure(),
		grpc.WithTimeout(10 * time.Second),
		grpc.WithBlock(),
		grpc.WithBackoffMaxDelay(1 * time.Second),
	}
	return opts
}

func main() {
	flag.Parse()

	start := time.Now()
	opts := getDialOptions()
	if *tls {
		cred, err := credentials.NewClientTLSFromFile(os.Getenv("HOME")+"/.homecontrol/server.crt", "Homecontrol")
		info := cred.Info()
		log.WithField("tls", true).
			WithField("tls_version", info.ProtocolVersion).
			WithField("tls_server_name", info.ServerName).
			Info("TLS Enabled")
		if err != nil {
			log.Fatalf("failed TLS: %v", err)
		}
		opts = append(opts, grpc.WithTransportCredentials(cred))
	} else {
		log.Warn("Insecure connection. No TLS")
		opts = append(opts, grpc.WithInsecure())
	}
	conn, err := grpc.Dial(*address, opts...)
	must(err)
	defer conn.Close()

	hue := hue.NewLightsClient(conn)
	ver := version.NewVersionClient(conn)

	log.WithField("address", *address).WithField("cmd", *cmd).WithField("group", *group).Info("Executing command")
	switch *cmd {
	case "on":
		switchOn(hue, *group)
	case "off":
		switchOff(hue, *group)
	case "blink":
		blink(hue, *group)
	case "sensors":
		resp := getSensors(hue)
		for _, sensor := range resp.Sensors {
			fmt.Printf("Name             : %s\n", sensor.GetName())
			fmt.Printf("ID               : %d\n", sensor.GetID())
			fmt.Printf("UniqueID         : %s\n", sensor.GetUniqueID())
			fmt.Printf("Type             : %s\n", sensor.GetType())
			fmt.Printf("State            : %v\n", sensor.GetState())
			fmt.Printf("Manufacturer Name: %s\n", sensor.GetManufacturerName())
			fmt.Printf("Model ID         : %s\n", sensor.GetModelID())
			fmt.Printf("Software Version : %s\n", sensor.GetSWVersion())
			fmt.Println()
		}
	case "version":
		log.WithField("version", version.Version).WithField("build", version.Build).Info("Client Version")
		resp, err := ver.Version(context.Background(), &version.VersionMessage{Version: version.Version, Build: version.Build})
		must(err)
		log.WithField("version", resp.Version).WithField("build", resp.Build).Info("Server Version")
	default:
		resp := getGroups(hue)
		log.Printf("Hue Light Groups: %v", resp.Groups)
	}

	end := time.Now()
	log.WithField("exec_time", end.Sub(start)).Printf("Execution finished")
}
<|endoftext|>