<commit_before>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// 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 defaultplugins

import (
	"context"

	"github.com/ligato/cn-infra/health/statuscheck"
	"github.com/ligato/cn-infra/health/statuscheck/model/status"
	intf "github.com/ligato/vpp-agent/plugins/defaultplugins/ifplugin/model/interfaces"
	"github.com/ligato/vpp-agent/plugins/defaultplugins/l2plugin/model/l2"
)

// Resync deletes obsolete operation status of network interfaces in DB.
// Obsolete state is one that is not part of SwIfIndex.
func (plugin *Plugin) resyncIfStateEvents(keys []string) error {
	for _, key := range keys {
		ifaceName, err := intf.ParseNameFromKey(key)
		if err != nil {
			return err
		}

		_, _, found := plugin.swIfIndexes.LookupIdx(ifaceName)
		if !found {
			err := plugin.PublishStatistics.Put(key, nil /*means delete*/)
			if err != nil {
				return err
			}
			plugin.Log.Debugf("Obsolete status for %v deleted", key)
		} else {
			plugin.Log.WithField("ifaceName", ifaceName).Debug("interface status is needed")
		}
	}

	return nil
}

// publishIfState goroutine is used to watch interface state notifications that are propagated to Messaging topic.
func (plugin *Plugin) publishIfStateEvents(ctx context.Context) {
	plugin.wg.Add(1)
	defer plugin.wg.Done()

	// store last errors to prevent repeating
	var lastPublishErr error
	var lastNotifErr error

	for {
		select {
		case ifState := <-plugin.ifStateChan:
			key := intf.InterfaceStateKey(ifState.State.Name)

			if plugin.PublishStatistics != nil {
				err := plugin.PublishStatistics.Put(key, ifState.State)
				if err != lastPublishErr {
					plugin.Log.Error(err)
				}
				lastPublishErr = err
			}

			// Marshall data into JSON & send kafka message.
			if plugin.ifStateNotifications != nil && ifState.Type == intf.UPDOWN {
				err := plugin.ifStateNotifications.Put(key, ifState.State)
				if err != lastNotifErr {
					plugin.Log.Error(err)
				}
				lastNotifErr = err
			}

			// Send interface state data to global agent status
			if plugin.statusCheckReg {
				plugin.StatusCheck.ReportStateChangeWithMeta(plugin.PluginName, statuscheck.OK, nil, &status.InterfaceStats_Interface{
					InternalName: ifState.State.InternalName,
					Index:        ifState.State.IfIndex,
					Status:       ifState.State.AdminStatus.String(),
					MacAddress:   ifState.State.PhysAddress,
				})
			}

		case <-ctx.Done():
			// Stop watching for state data updates.
			return
		}
	}
}

// Resync deletes old operation status of bridge domains in ETCD.
func (plugin *Plugin) resyncBdStateEvents(keys []string) error {
	for _, key := range keys {
		bdName, err := intf.ParseNameFromKey(key)
		if err != nil {
			return err
		}
		_, _, found := plugin.bdIndexes.LookupIdx(bdName)
		if !found {
			err := plugin.PublishStatistics.Put(key, nil)
			if err != nil {
				return err
			}
			plugin.Log.Debugf("Obsolete status for %v deleted", key)
		} else {
			plugin.Log.WithField("bdName", bdName).Debug("bridge domain status required")
		}
	}

	return nil
}

// PublishBdState is used to watch bridge domain state notifications.
func (plugin *Plugin) publishBdStateEvents(ctx context.Context) {
	plugin.wg.Add(1)
	defer plugin.wg.Done()

	for {
		select {
		case bdState := <-plugin.bdStateChan:
			if bdState != nil && bdState.State != nil && plugin.Publish != nil {
				key := l2.BridgeDomainStateKey(bdState.State.InternalName)
				// Remove BD state
				if bdState.State.Index == 0 && bdState.State.InternalName != "" {
					plugin.PublishStatistics.Put(key, nil)
					plugin.Log.Debugf("Bridge domain %v: state removed from ETCD", bdState.State.InternalName)
					// Write/Update BD state
				} else if bdState.State.Index != 0 {
					plugin.PublishStatistics.Put(key, bdState.State)
					plugin.Log.Debugf("Bridge domain %v: state stored in ETCD", bdState.State.InternalName)
				} else {
					plugin.Log.Warnf("Unable to process bridge domain state with Idx %v and Name %v",
						bdState.State.Index, bdState.State.InternalName)
				}
			}
		case <-ctx.Done():
			// Stop watching for state data updates.
			return
		}
	}
}
<commit_msg>publish if state events compares errors as strings<commit_after>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// 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 defaultplugins

import (
	"context"

	"github.com/ligato/cn-infra/health/statuscheck"
	"github.com/ligato/cn-infra/health/statuscheck/model/status"
	intf "github.com/ligato/vpp-agent/plugins/defaultplugins/ifplugin/model/interfaces"
	"github.com/ligato/vpp-agent/plugins/defaultplugins/l2plugin/model/l2"
)

// Resync deletes obsolete operation status of network interfaces in DB.
// Obsolete state is one that is not part of SwIfIndex.
func (plugin *Plugin) resyncIfStateEvents(keys []string) error {
	for _, key := range keys {
		ifaceName, err := intf.ParseNameFromKey(key)
		if err != nil {
			return err
		}

		_, _, found := plugin.swIfIndexes.LookupIdx(ifaceName)
		if !found {
			err := plugin.PublishStatistics.Put(key, nil /*means delete*/)
			if err != nil {
				return err
			}
			plugin.Log.Debugf("Obsolete status for %v deleted", key)
		} else {
			plugin.Log.WithField("ifaceName", ifaceName).Debug("interface status is needed")
		}
	}

	return nil
}

// publishIfState goroutine is used to watch interface state notifications that are propagated to Messaging topic.
func (plugin *Plugin) publishIfStateEvents(ctx context.Context) {
	plugin.wg.Add(1)
	defer plugin.wg.Done()

	// store last errors to prevent repeating
	var lastPublishErr error
	var lastNotifErr error

	for {
		select {
		case ifState := <-plugin.ifStateChan:
			key := intf.InterfaceStateKey(ifState.State.Name)

			if plugin.PublishStatistics != nil {
				err := plugin.PublishStatistics.Put(key, ifState.State)
				if err != nil {
					if lastPublishErr == nil || lastPublishErr.Error() != err.Error() {
						plugin.Log.Error(err)
					}
				}
				lastPublishErr = err
			}

			// Marshall data into JSON & send kafka message.
			if plugin.ifStateNotifications != nil && ifState.Type == intf.UPDOWN {
				err := plugin.ifStateNotifications.Put(key, ifState.State)
				if err != nil {
					if lastNotifErr == nil || lastNotifErr.Error() != err.Error() {
						plugin.Log.Error(err)
					}
				}
				lastNotifErr = err
			}

			// Send interface state data to global agent status
			if plugin.statusCheckReg {
				plugin.StatusCheck.ReportStateChangeWithMeta(plugin.PluginName, statuscheck.OK, nil, &status.InterfaceStats_Interface{
					InternalName: ifState.State.InternalName,
					Index:        ifState.State.IfIndex,
					Status:       ifState.State.AdminStatus.String(),
					MacAddress:   ifState.State.PhysAddress,
				})
			}

		case <-ctx.Done():
			// Stop watching for state data updates.
			return
		}
	}
}

// Resync deletes old operation status of bridge domains in ETCD.
func (plugin *Plugin) resyncBdStateEvents(keys []string) error {
	for _, key := range keys {
		bdName, err := intf.ParseNameFromKey(key)
		if err != nil {
			return err
		}
		_, _, found := plugin.bdIndexes.LookupIdx(bdName)
		if !found {
			err := plugin.PublishStatistics.Put(key, nil)
			if err != nil {
				return err
			}
			plugin.Log.Debugf("Obsolete status for %v deleted", key)
		} else {
			plugin.Log.WithField("bdName", bdName).Debug("bridge domain status required")
		}
	}

	return nil
}

// PublishBdState is used to watch bridge domain state notifications.
func (plugin *Plugin) publishBdStateEvents(ctx context.Context) {
	plugin.wg.Add(1)
	defer plugin.wg.Done()

	for {
		select {
		case bdState := <-plugin.bdStateChan:
			if bdState != nil && bdState.State != nil && plugin.Publish != nil {
				key := l2.BridgeDomainStateKey(bdState.State.InternalName)
				// Remove BD state
				if bdState.State.Index == 0 && bdState.State.InternalName != "" {
					plugin.PublishStatistics.Put(key, nil)
					plugin.Log.Debugf("Bridge domain %v: state removed from ETCD", bdState.State.InternalName)
					// Write/Update BD state
				} else if bdState.State.Index != 0 {
					plugin.PublishStatistics.Put(key, bdState.State)
					plugin.Log.Debugf("Bridge domain %v: state stored in ETCD", bdState.State.InternalName)
				} else {
					plugin.Log.Warnf("Unable to process bridge domain state with Idx %v and Name %v",
						bdState.State.Index, bdState.State.InternalName)
				}
			}
		case <-ctx.Done():
			// Stop watching for state data updates.
			return
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package sessionaffinity

import (
	"regexp"

	networking "k8s.io/api/networking/v1beta1"
	"k8s.io/klog"

	"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
	"k8s.io/ingress-nginx/internal/ingress/resolver"
)

const (
	annotationAffinityType = "affinity"
	annotationAffinityMode = "affinity-mode"
	// If a cookie with this name exists,
	// its value is used as an index into the list of available backends.
	annotationAffinityCookieName = "session-cookie-name"

	defaultAffinityCookieName = "INGRESSCOOKIE"

	// This is used to control the cookie expires, its value is a number of seconds until the
	// cookie expires
	annotationAffinityCookieExpires = "session-cookie-expires"

	// This is used to control the cookie expires, its value is a number of seconds until the
	// cookie expires
	annotationAffinityCookieMaxAge = "session-cookie-max-age"

	// This is used to control the cookie path when use-regex is set to true
	annotationAffinityCookiePath = "session-cookie-path"

	// This is used to control the SameSite attribute of the cookie
	annotationAffinityCookieSameSite = "session-cookie-samesite"

	// This is used to control whether SameSite=None should be conditionally applied based on the User-Agent
	annotationAffinityCookieConditionalSameSiteNone = "session-cookie-conditional-samesite-none"

	// This is used to control the cookie change after request failure
	annotationAffinityCookieChangeOnFailure = "session-cookie-change-on-failure"
)

var (
	affinityCookieExpiresRegex = regexp.MustCompile(`(^0|-?[1-9]\d*$)`)
)

// Config describes the per ingress session affinity config
type Config struct {
	// The type of affinity that will be used
	Type string `json:"type"`
	// The affinity mode, i.e. how sticky a session is
	Mode string `json:"mode"`
	Cookie
}

// Cookie describes the Config of cookie type affinity
type Cookie struct {
	// The name of the cookie that will be used in case of cookie affinity type.
	Name string `json:"name"`
	// The time duration to control cookie expires
	Expires string `json:"expires"`
	// The number of seconds until the cookie expires
	MaxAge string `json:"maxage"`
	// The path that a cookie will be set on
	Path string `json:"path"`
	// Flag that allows cookie regeneration on request failure
	ChangeOnFailure bool `json:"changeonfailure"`
	// SameSite attribute value
	SameSite string `json:"samesite"`
	// Flag that conditionally applies SameSite=None attribute on cookie if user agent accepts it.
	ConditionalSameSiteNone bool `json:"conditional-samesite-none"`
}

// cookieAffinityParse gets the annotation values related to Cookie Affinity
// It also sets default values when no value or incorrect value is found
func (a affinity) cookieAffinityParse(ing *networking.Ingress) *Cookie {
	var err error

	cookie := &Cookie{}

	cookie.Name, err = parser.GetStringAnnotation(annotationAffinityCookieName, ing)
	if err != nil {
		klog.V(3).Infof("Ingress %v: No value found in annotation %v. Using the default %v", ing.Name, annotationAffinityCookieName, defaultAffinityCookieName)
		cookie.Name = defaultAffinityCookieName
	}

	cookie.Expires, err = parser.GetStringAnnotation(annotationAffinityCookieExpires, ing)
	if err != nil || !affinityCookieExpiresRegex.MatchString(cookie.Expires) {
		klog.V(3).Infof("Invalid or no annotation value found in Ingress %v: %v. Ignoring it", ing.Name, annotationAffinityCookieExpires)
		cookie.Expires = ""
	}

	cookie.MaxAge, err = parser.GetStringAnnotation(annotationAffinityCookieMaxAge, ing)
	if err != nil || !affinityCookieExpiresRegex.MatchString(cookie.MaxAge) {
		klog.V(3).Infof("Invalid or no annotation value found in Ingress %v: %v. Ignoring it", ing.Name, annotationAffinityCookieMaxAge)
		cookie.MaxAge = ""
	}

	cookie.Path, err = parser.GetStringAnnotation(annotationAffinityCookiePath, ing)
	if err != nil {
		klog.V(3).Infof("Invalid or no annotation value found in Ingress %v: %v. Ignoring it", ing.Name, annotationAffinityCookieMaxAge)
	}

	cookie.SameSite, err = parser.GetStringAnnotation(annotationAffinityCookieSameSite, ing)
	if err != nil {
		klog.V(3).Infof("Invalid or no annotation value found in Ingress %v: %v. Ignoring it", ing.Name, annotationAffinityCookieSameSite)
	}

	cookie.ConditionalSameSiteNone, err = parser.GetBoolAnnotation(annotationAffinityCookieConditionalSameSiteNone, ing)
	if err != nil {
		klog.V(3).Infof("Invalid or no annotation value found in Ingress %v: %v. Ignoring it", ing.Name, annotationAffinityCookieConditionalSameSiteNone)
	}

	cookie.ChangeOnFailure, err = parser.GetBoolAnnotation(annotationAffinityCookieChangeOnFailure, ing)
	if err != nil {
		klog.V(3).Infof("Invalid or no annotation value found in Ingress %v: %v. Ignoring it", ing.Name, annotationAffinityCookieChangeOnFailure)
	}

	cookie.ChangeOnFailure, err = parser.GetBoolAnnotation(annotationAffinityCookieChangeOnFailure, ing)
	if err != nil {
		klog.V(3).Infof("Invalid or no annotation value found in Ingress %v: %v. Ignoring it", ing.Name, annotationAffinityCookieChangeOnFailure)
	}

	return cookie
}

// NewParser creates a new Affinity annotation parser
func NewParser(r resolver.Resolver) parser.IngressAnnotation {
	return affinity{r}
}

type affinity struct {
	r resolver.Resolver
}

// ParseAnnotations parses the annotations contained in the ingress
// rule used to configure the affinity directives
func (a affinity) Parse(ing *networking.Ingress) (interface{}, error) {
	cookie := &Cookie{}
	// Check the type of affinity that will be used
	at, err := parser.GetStringAnnotation(annotationAffinityType, ing)
	if err != nil {
		at = ""
	}

	// Check the afinity mode that will be used
	am, err := parser.GetStringAnnotation(annotationAffinityMode, ing)
	if err != nil {
		am = ""
	}

	switch at {
	case "cookie":
		cookie = a.cookieAffinityParse(ing)
	default:
		klog.V(3).Infof("No default affinity was found for Ingress %v", ing.Name)

	}

	return &Config{
		Type:   at,
		Mode:   am,
		Cookie: *cookie,
	}, nil
}
<commit_msg>Remove duplicate annotation parsing for annotationAffinityCookieChangeOnFailure<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package sessionaffinity

import (
	"regexp"

	networking "k8s.io/api/networking/v1beta1"
	"k8s.io/klog"

	"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
	"k8s.io/ingress-nginx/internal/ingress/resolver"
)

const (
	annotationAffinityType = "affinity"
	annotationAffinityMode = "affinity-mode"
	// If a cookie with this name exists,
	// its value is used as an index into the list of available backends.
	annotationAffinityCookieName = "session-cookie-name"

	defaultAffinityCookieName = "INGRESSCOOKIE"

	// This is used to control the cookie expires, its value is a number of seconds until the
	// cookie expires
	annotationAffinityCookieExpires = "session-cookie-expires"

	// This is used to control the cookie expires, its value is a number of seconds until the
	// cookie expires
	annotationAffinityCookieMaxAge = "session-cookie-max-age"

	// This is used to control the cookie path when use-regex is set to true
	annotationAffinityCookiePath = "session-cookie-path"

	// This is used to control the SameSite attribute of the cookie
	annotationAffinityCookieSameSite = "session-cookie-samesite"

	// This is used to control whether SameSite=None should be conditionally applied based on the User-Agent
	annotationAffinityCookieConditionalSameSiteNone = "session-cookie-conditional-samesite-none"

	// This is used to control the cookie change after request failure
	annotationAffinityCookieChangeOnFailure = "session-cookie-change-on-failure"
)

var (
	affinityCookieExpiresRegex = regexp.MustCompile(`(^0|-?[1-9]\d*$)`)
)

// Config describes the per ingress session affinity config
type Config struct {
	// The type of affinity that will be used
	Type string `json:"type"`
	// The affinity mode, i.e. how sticky a session is
	Mode string `json:"mode"`
	Cookie
}

// Cookie describes the Config of cookie type affinity
type Cookie struct {
	// The name of the cookie that will be used in case of cookie affinity type.
	Name string `json:"name"`
	// The time duration to control cookie expires
	Expires string `json:"expires"`
	// The number of seconds until the cookie expires
	MaxAge string `json:"maxage"`
	// The path that a cookie will be set on
	Path string `json:"path"`
	// Flag that allows cookie regeneration on request failure
	ChangeOnFailure bool `json:"changeonfailure"`
	// SameSite attribute value
	SameSite string `json:"samesite"`
	// Flag that conditionally applies SameSite=None attribute on cookie if user agent accepts it.
	ConditionalSameSiteNone bool `json:"conditional-samesite-none"`
}

// cookieAffinityParse gets the annotation values related to Cookie Affinity
// It also sets default values when no value or incorrect value is found
func (a affinity) cookieAffinityParse(ing *networking.Ingress) *Cookie {
	var err error

	cookie := &Cookie{}

	cookie.Name, err = parser.GetStringAnnotation(annotationAffinityCookieName, ing)
	if err != nil {
		klog.V(3).Infof("Ingress %v: No value found in annotation %v. Using the default %v", ing.Name, annotationAffinityCookieName, defaultAffinityCookieName)
		cookie.Name = defaultAffinityCookieName
	}

	cookie.Expires, err = parser.GetStringAnnotation(annotationAffinityCookieExpires, ing)
	if err != nil || !affinityCookieExpiresRegex.MatchString(cookie.Expires) {
		klog.V(3).Infof("Invalid or no annotation value found in Ingress %v: %v. Ignoring it", ing.Name, annotationAffinityCookieExpires)
		cookie.Expires = ""
	}

	cookie.MaxAge, err = parser.GetStringAnnotation(annotationAffinityCookieMaxAge, ing)
	if err != nil || !affinityCookieExpiresRegex.MatchString(cookie.MaxAge) {
		klog.V(3).Infof("Invalid or no annotation value found in Ingress %v: %v. Ignoring it", ing.Name, annotationAffinityCookieMaxAge)
		cookie.MaxAge = ""
	}

	cookie.Path, err = parser.GetStringAnnotation(annotationAffinityCookiePath, ing)
	if err != nil {
		klog.V(3).Infof("Invalid or no annotation value found in Ingress %v: %v. Ignoring it", ing.Name, annotationAffinityCookieMaxAge)
	}

	cookie.SameSite, err = parser.GetStringAnnotation(annotationAffinityCookieSameSite, ing)
	if err != nil {
		klog.V(3).Infof("Invalid or no annotation value found in Ingress %v: %v. Ignoring it", ing.Name, annotationAffinityCookieSameSite)
	}

	cookie.ConditionalSameSiteNone, err = parser.GetBoolAnnotation(annotationAffinityCookieConditionalSameSiteNone, ing)
	if err != nil {
		klog.V(3).Infof("Invalid or no annotation value found in Ingress %v: %v. Ignoring it", ing.Name, annotationAffinityCookieConditionalSameSiteNone)
	}

	cookie.ChangeOnFailure, err = parser.GetBoolAnnotation(annotationAffinityCookieChangeOnFailure, ing)
	if err != nil {
		klog.V(3).Infof("Invalid or no annotation value found in Ingress %v: %v. Ignoring it", ing.Name, annotationAffinityCookieChangeOnFailure)
	}

	return cookie
}

// NewParser creates a new Affinity annotation parser
func NewParser(r resolver.Resolver) parser.IngressAnnotation {
	return affinity{r}
}

type affinity struct {
	r resolver.Resolver
}

// ParseAnnotations parses the annotations contained in the ingress
// rule used to configure the affinity directives
func (a affinity) Parse(ing *networking.Ingress) (interface{}, error) {
	cookie := &Cookie{}
	// Check the type of affinity that will be used
	at, err := parser.GetStringAnnotation(annotationAffinityType, ing)
	if err != nil {
		at = ""
	}

	// Check the afinity mode that will be used
	am, err := parser.GetStringAnnotation(annotationAffinityMode, ing)
	if err != nil {
		am = ""
	}

	switch at {
	case "cookie":
		cookie = a.cookieAffinityParse(ing)
	default:
		klog.V(3).Infof("No default affinity was found for Ingress %v", ing.Name)

	}

	return &Config{
		Type:   at,
		Mode:   am,
		Cookie: *cookie,
	}, nil
}
<|endoftext|>


<commit_before>package qy

import (
	"strings"

	"github.com/bigwhite/gowechat/pb"
)

const (
	sendURL = "https://qyapi.weixin.qq.com/cgi-bin/message/send"
)

type SendMsgTextPkg struct {
	pb.SendMsgTextPkg
	ToParty string `json:"toparty,omitempty"`
	ToTag   string `json:"totag,omitempty"`
	AgentID string `json:"agentid"`
	Safe    string `json:"safe,omitempty"`
}

type SendMsgImagePkg struct {
	pb.SendMsgImagePkg
	ToParty string `json:"toparty,omitempty"`
	ToTag   string `json:"totag,omitempty"`
	AgentID string `json:"agentid"`
	Safe    string `json:"safe,omitempty"`
}

type Artical struct {
	Title       string `json:"title,omitempty"`
	Description string `json:"description,omitempty"`
	Url         string `json:"url,omitempty"`
	PicUrl      string `json:"picurl,omitempty"`
}

type SendMsgNewsPkg struct {
	ToUserName string    `json:"touser"`
	ToParty    string    `json:"toparty,omitempty"`
	ToTag      string    `json:"totag,omitempty"`
	MsgType    string    `json:"msgtype"`
	AgentID    string    `json:"agentid"`
	News       []Artical `json:"news"`
}

func SendMsg(accessToken string, pkg interface{}) error {
	r := strings.Join([]string{sendURL, "?access_token=", accessToken}, "")
	return pb.SendMsg(r, pkg)
}
<commit_msg>fix bug in send news msg of qy<commit_after>package qy

import (
	"strings"

	"github.com/bigwhite/gowechat/pb"
)

const (
	sendURL = "https://qyapi.weixin.qq.com/cgi-bin/message/send"
)

type SendMsgTextPkg struct {
	pb.SendMsgTextPkg
	ToParty string `json:"toparty,omitempty"`
	ToTag   string `json:"totag,omitempty"`
	AgentID string `json:"agentid"`
	Safe    string `json:"safe,omitempty"`
}

type SendMsgImagePkg struct {
	pb.SendMsgImagePkg
	ToParty string `json:"toparty,omitempty"`
	ToTag   string `json:"totag,omitempty"`
	AgentID string `json:"agentid"`
	Safe    string `json:"safe,omitempty"`
}

type Artical struct {
	Title       string `json:"title,omitempty"`
	Description string `json:"description,omitempty"`
	Url         string `json:"url,omitempty"`
	PicUrl      string `json:"picurl,omitempty"`
}

type Articals struct {
	Arcs []Artical `json:"articals"`
}
type SendMsgNewsPkg struct {
	ToUserName string   `json:"touser"`
	ToParty    string   `json:"toparty,omitempty"`
	ToTag      string   `json:"totag,omitempty"`
	MsgType    string   `json:"msgtype"`
	AgentID    string   `json:"agentid"`
	News       Articals `json:"news"`
}

func SendMsg(accessToken string, pkg interface{}) error {
	r := strings.Join([]string{sendURL, "?access_token=", accessToken}, "")
	return pb.SendMsg(r, pkg)
}
<|endoftext|>


<commit_before>package utils

import (
	"testing"
	"time"
)

func TestParseClamAVTimeStamp(t *testing.T) {
	expected, err := time.Parse(time.RFC3339, "2006-01-02T15:04:00-07:00")

	if err != nil {
		t.Error(err)
	}

	parsed, err := ParseClamAVTimeStamp("02 Jan 2006 15:04 -0700")

	if err != nil {
		t.Error(err)
	}

	if !expected.Equal(parsed) {
		t.Errorf("Timestamps are not equal.\n"+
			"Expected: %v\n"+
			"Actual  : %v",
			expected, parsed)
	}
}
<commit_msg>Added additional time utils test<commit_after>package utils

import (
	"testing"
	"time"
)

func TestValidParseClamAVTimeStamp(t *testing.T) {
	expected, err := time.Parse(time.RFC3339, "2006-01-02T15:04:00-07:00")

	if err != nil {
		t.Error(err)
	}

	parsed, err := ParseClamAVTimeStamp("02 Jan 2006 15:04 -0700")

	if err != nil {
		t.Error(err)
	}

	if !expected.Equal(parsed) {
		t.Errorf("Timestamps are not equal.\n"+
			"Expected: %v\n"+
			"Actual  : %v",
			expected, parsed)
	}
}

func TestInvalidParseClamAVTimeStamp(t *testing.T) {
	_, err := ParseClamAVTimeStamp("02 Jan 2006 15:04Z")

	if err == nil {
		t.Error("Expected time parsing exception not thrown")
	}
}
<|endoftext|>


<commit_before>package forecasting

import (
	"io"
	"draringi/codejam2013/src/data"
	"strconv"
)

func buildDataToGuess (data []data.Record) (inputs [][]interface{}){
	for i := 0; i<len(data); i++ {
		if data[i].Null {
			row := make([]interface{},5)
			row[0]=data[i].Time
			row[1]=data[i].Radiation
			row[2]=data[i].Humidity
			row[3]=data[i].Temperature
			row[4]=data[i].Wind
			inputs = append(inputs,row)
		}
	}
	return
}

func PredictCSV (file io.Reader, channel chan *data.CSVRequest) *data.CSVData {
	forest := learnCSV(file, channel)
	ret := make(chan (*data.CSVData), 1)
	request := new(data.CSVRequest)
	request.Return = ret
	request.Request = file
	channel <- request
	resp := new(data.CSVData)
	for {
		resp = <-ret
		if resp != nil {
			break
		}
	}
	inputs := buildDataToGuess(resp.Data)
	var outputs []string
	for i := 0; i<len(inputs); i++ {
		outputs = append (outputs, forest.Predicate(inputs[i]))
	}
	k:=0
	for i := 0; i<len(resp.Data); i++ {
		if resp.Data[i].Null {
			resp.Data[i].Power, _ = strconv.ParseFloat(outputs[k], 64)
			k++
			resp.Data[i].Null = false
		}
	}
	return resp
}

func PredictCSVSingle (file io.Reader) *data.CSVData {
	forest := learnCSVSingle(file)
	resp := new(data.CSVData)
	resp.Labels, resp.Data := data.CSVParse(file)
	inputs := buildDataToGuess(resp.Data)
	var outputs []string
	for i := 0; i<len(inputs); i++ {
		outputs = append (outputs, forest.Predicate(inputs[i]))
	}
	k:=0
	for i := 0; i<len(resp.Data); i++ {
		if resp.Data[i].Null {
			resp.Data[i].Power, _ = strconv.ParseFloat(outputs[k], 64)
			k++
			resp.Data[i].Null = false
		}
	}
	return resp
}

<commit_msg>fixed association<commit_after>package forecasting

import (
	"io"
	"draringi/codejam2013/src/data"
	"strconv"
)

func buildDataToGuess (data []data.Record) (inputs [][]interface{}){
	for i := 0; i<len(data); i++ {
		if data[i].Null {
			row := make([]interface{},5)
			row[0]=data[i].Time
			row[1]=data[i].Radiation
			row[2]=data[i].Humidity
			row[3]=data[i].Temperature
			row[4]=data[i].Wind
			inputs = append(inputs,row)
		}
	}
	return
}

func PredictCSV (file io.Reader, channel chan *data.CSVRequest) *data.CSVData {
	forest := learnCSV(file, channel)
	ret := make(chan (*data.CSVData), 1)
	request := new(data.CSVRequest)
	request.Return = ret
	request.Request = file
	channel <- request
	resp := new(data.CSVData)
	for {
		resp = <-ret
		if resp != nil {
			break
		}
	}
	inputs := buildDataToGuess(resp.Data)
	var outputs []string
	for i := 0; i<len(inputs); i++ {
		outputs = append (outputs, forest.Predicate(inputs[i]))
	}
	k:=0
	for i := 0; i<len(resp.Data); i++ {
		if resp.Data[i].Null {
			resp.Data[i].Power, _ = strconv.ParseFloat(outputs[k], 64)
			k++
			resp.Data[i].Null = false
		}
	}
	return resp
}

func PredictCSVSingle (file io.Reader) *data.CSVData {
	forest := learnCSVSingle(file)
	resp := new(data.CSVData)
	resp.Labels, resp.Data = data.CSVParse(file)
	inputs := buildDataToGuess(resp.Data)
	var outputs []string
	for i := 0; i<len(inputs); i++ {
		outputs = append (outputs, forest.Predicate(inputs[i]))
	}
	k:=0
	for i := 0; i<len(resp.Data); i++ {
		if resp.Data[i].Null {
			resp.Data[i].Power, _ = strconv.ParseFloat(outputs[k], 64)
			k++
			resp.Data[i].Null = false
		}
	}
	return resp
}

<|endoftext|>


<commit_before><commit_msg>Add a unit test to try to reproduce pivotal #117859291.  It seems to be working fine, but keep the unit test since its a good use case anyway.<commit_after><|endoftext|>


<commit_before>/*
	Used by the Leasing Server to interact with swarming.
*/
package main

import (
	"context"
	"fmt"
	"strings"

	swarming_api "go.chromium.org/luci/common/api/swarming/swarming/v1"
	"google.golang.org/api/compute/v1"

	"go.skia.org/infra/go/auth"
	"go.skia.org/infra/go/baseapp"
	"go.skia.org/infra/go/cas"
	"go.skia.org/infra/go/cas/rbe"
	"go.skia.org/infra/go/httputils"
	"go.skia.org/infra/go/skerr"
	"go.skia.org/infra/go/swarming"
	"go.skia.org/infra/leasing/go/types"
)

// SwarmingInstanceClients contains all of the API clients needed to interact
// with a given Swarming instance.
type SwarmingInstanceClients struct {
	SwarmingServer string
	SwarmingClient *swarming.ApiClient

	CasClient   *cas.CAS
	CasInstance string
}

var (
	casClientPublic  cas.CAS
	casClientPrivate cas.CAS

	swarmingClientPublic  swarming.ApiClient
	swarmingClientPrivate swarming.ApiClient

	// PublicSwarming contains the API clients needed for the public Swarming
	// instance.
	PublicSwarming *SwarmingInstanceClients = &SwarmingInstanceClients{
		SwarmingServer: swarming.SWARMING_SERVER,
		SwarmingClient: &swarmingClientPublic,
		CasClient:      &casClientPublic,
		CasInstance:    rbe.InstanceChromiumSwarm,
	}

	// InternalSwarming contains the API clients needed for the internal
	// Swarming instance.
	InternalSwarming *SwarmingInstanceClients = &SwarmingInstanceClients{
		SwarmingServer: swarming.SWARMING_SERVER_PRIVATE,
		SwarmingClient: &swarmingClientPrivate,
		CasClient:      &casClientPrivate,
		CasInstance:    rbe.InstanceChromeSwarming,
	}

	// PoolsToSwarmingInstance maps Swarming pool names to Swarming instances.
	PoolsToSwarmingInstance = map[string]*SwarmingInstanceClients{
		"Skia":             PublicSwarming,
		"SkiaCT":           PublicSwarming,
		"SkiaInternal":     InternalSwarming,
		"CT":               InternalSwarming,
		"CTAndroidBuilder": InternalSwarming,
		"CTLinuxBuilder":   InternalSwarming,
	}

	cpythonPackage = &swarming_api.SwarmingRpcsCipdPackage{
		PackageName: "infra/python/cpython/${platform}",
		Path:        "python",
		Version:     "version:2.7.14.chromium14",
	}
)

// SwarmingInit initializes Swarming globally.
func SwarmingInit(serviceAccountFile string) error {
	ts, err := auth.NewDefaultTokenSource(*baseapp.Local, swarming.AUTH_SCOPE, compute.CloudPlatformScope)
	if err != nil {
		return skerr.Wrapf(err, "Problem setting up default token source")
	}

	// Public CAS client.
	casClientPublic, err = rbe.NewClient(context.TODO(), rbe.InstanceChromiumSwarm, ts)
	if err != nil {
		return skerr.Wrapf(err, "Failed to create RBE client")
	}

	// Private CAS client.
	casClientPrivate, err = rbe.NewClient(context.TODO(), rbe.InstanceChromeSwarming, ts)
	if err != nil {
		return skerr.Wrapf(err, "Failed to create RBE client")
	}

	// Authenticated HTTP client.
	httpClient := httputils.DefaultClientConfig().WithTokenSource(ts).With2xxOnly().Client()

	// Public Swarming API client.
	swarmingClientPublic, err = swarming.NewApiClient(httpClient, swarming.SWARMING_SERVER)
	if err != nil {
		return skerr.Wrapf(err, "Failed to create public swarming client")
	}
	// Private Swarming API client.
	swarmingClientPrivate, err = swarming.NewApiClient(httpClient, swarming.SWARMING_SERVER_PRIVATE)
	if err != nil {
		return skerr.Wrapf(err, "Failed to create private swarming client")
	}

	return nil
}

// GetSwarmingInstance returns the Swarming instance for the given Swarming
// pool.
func GetSwarmingInstance(pool string) *SwarmingInstanceClients {
	return PoolsToSwarmingInstance[pool]
}

// GetSwarmingClient returns the Swarming client for the given Swarming pool.
func GetSwarmingClient(pool string) *swarming.ApiClient {
	return GetSwarmingInstance(pool).SwarmingClient
}

// GetCASClient returns the CAS client for the given Swarming pool.
func GetCASClient(pool string) *cas.CAS {
	return GetSwarmingInstance(pool).CasClient
}

func getPoolDetails(ctx context.Context, pool string) (*types.PoolDetails, error) {
	swarmingClient := *GetSwarmingClient(pool)
	bots, err := swarmingClient.ListBotsForPool(ctx, pool)
	if err != nil {
		return nil, fmt.Errorf("Could not list bots in pool: %s", err)
	}
	osTypes := map[string]int{}
	osToDeviceTypes := map[string]map[string]int{}
	for _, bot := range bots {
		if bot.IsDead || bot.Quarantined {
			// Do not include dead/quarantined bots in the counts below.
			continue
		}
		osType := ""
		deviceType := ""
		for _, d := range bot.Dimensions {
			if d.Key == "os" {
				val := ""
				// Use the longest string from the os values because that is what the swarming UI
				// does and it works in all cases we have (atleast as of 11/1/17).
				for _, v := range d.Value {
					if len(v) > len(val) {
						val = v
					}
				}
				osType = val
			}
			if d.Key == "device_type" {
				// There should only be one value for device type.
				deviceType = d.Value[0]
			}
		}
		osTypes[osType]++
		if _, ok := osToDeviceTypes[osType]; !ok {
			osToDeviceTypes[osType] = map[string]int{}
		}
		if deviceType != "" {
			osToDeviceTypes[osType][deviceType]++
		}
	}
	return &types.PoolDetails{
		OsTypes:         osTypes,
		OsToDeviceTypes: osToDeviceTypes,
	}, nil
}

// GetDetailsOfAllPools returns details for each of the known Swarming pools.
func GetDetailsOfAllPools(ctx context.Context) (map[string]*types.PoolDetails, error) {
	poolToDetails := map[string]*types.PoolDetails{}
	for pool := range PoolsToSwarmingInstance {
		details, err := getPoolDetails(ctx, pool)
		if err != nil {
			return nil, err
		}
		poolToDetails[pool] = details
	}
	return poolToDetails, nil
}

// AddLeasingArtifactsToCAS uploads the leasing artifacts and merges them into
// the given CAS input if it exists.
func AddLeasingArtifactsToCAS(ctx context.Context, pool string, casInput *swarming_api.SwarmingRpcsCASReference) (string, error) {
	client := *GetCASClient(pool)

	// Upload the leasing artifacts.
	// TODO(rmistry): After this has been done once, we should be able to just
	// use the digest as a constant.
	leasingScriptDigest, err := client.Upload(ctx, *artifactsDir, []string{"leasing.py"}, nil)
	if err != nil {
		return "", skerr.Wrap(err)
	}
	if casInput == nil {
		return leasingScriptDigest, nil
	}
	return client.Merge(ctx, []string{leasingScriptDigest, rbe.DigestToString(casInput.Digest.Hash, casInput.Digest.SizeBytes)})
}

// GetSwarmingTask retrieves the given Swarming task.
func GetSwarmingTask(ctx context.Context, pool, taskID string) (*swarming_api.SwarmingRpcsTaskResult, error) {
	swarmingClient := *GetSwarmingClient(pool)
	return swarmingClient.GetTask(ctx, taskID, false)
}

// GetSwarmingTaskMetadata returns the metadata for the given Swarming task.
func GetSwarmingTaskMetadata(ctx context.Context, pool, taskID string) (*swarming_api.SwarmingRpcsTaskRequestMetadata, error) {
	swarmingClient := *GetSwarmingClient(pool)
	return swarmingClient.GetTaskMetadata(ctx, taskID)
}

// IsBotIDValid returns true iff the given bot exists in the given pool.
func IsBotIDValid(ctx context.Context, pool, botID string) (bool, error) {
	swarmingClient := *GetSwarmingClient(pool)
	dims := map[string]string{
		"pool": pool,
		"id":   botID,
	}
	bots, err := swarmingClient.ListBots(ctx, dims)
	if err != nil {
		return false, fmt.Errorf("Could not query swarming bots with %s: %s", dims, err)
	}
	if len(bots) > 1 {
		return false, fmt.Errorf("Something went wrong, more than 1 bot was returned with %s: %s", dims, err)
	}
	if len(bots) == 0 {
		// There were no matches for the pool + botId combination.
		return false, nil
	}
	if bots[0].BotId == botID {
		return true, nil
	}
	return false, fmt.Errorf("%s returned %s instead of the expected %s", dims, bots[1].BotId, botID)
}

// TriggerSwarmingTask triggers the given Swarming task.
func TriggerSwarmingTask(ctx context.Context, pool, requester, datastoreID, osType, deviceType, botID, serverURL, casDigest, relativeCwd string, cipdInput *swarming_api.SwarmingRpcsCipdInput, cmd []string) (string, error) {
	dimsMap := map[string]string{
		"pool": pool,
	}
	if osType != "" {
		dimsMap["os"] = osType
	}
	if deviceType != "" {
		dimsMap["device_type"] = deviceType
	}
	if botID != "" {
		dimsMap["id"] = botID
	}
	dims := make([]*swarming_api.SwarmingRpcsStringPair, 0, len(dimsMap))
	for k, v := range dimsMap {
		dims = append(dims, &swarming_api.SwarmingRpcsStringPair{
			Key:   k,
			Value: v,
		})
	}

	// Always include cpython for Windows. See skbug.com/9501 for context and
	// for why we do not include it for all architectures.
	pythonBinary := "python"
	if strings.HasPrefix(osType, "Windows") {
		if cipdInput == nil {
			cipdInput = &swarming_api.SwarmingRpcsCipdInput{}
		}
		if cipdInput.Packages == nil {
			cipdInput.Packages = []*swarming_api.SwarmingRpcsCipdPackage{cpythonPackage}
		} else {
			cipdInput.Packages = append(cipdInput.Packages, cpythonPackage)
		}
		pythonBinary = "python/bin/python"
	}

	// Arguments that will be passed to leasing.py
	extraArgs := []string{
		"--task-id", datastoreID,
		"--os-type", osType,
		"--leasing-server", serverURL,
		"--debug-command", strings.Join(cmd, " "),
		"--command-relative-dir", relativeCwd,
	}

	// Construct the command.
	command := []string{pythonBinary, "leasing.py"}
	command = append(command, extraArgs...)

	swarmingInstance := GetSwarmingInstance(pool)
	expirationSecs := int64(swarming.RECOMMENDED_EXPIRATION.Seconds())
	executionTimeoutSecs := int64(swarmingHardTimeout.Seconds())
	ioTimeoutSecs := int64(swarmingHardTimeout.Seconds())
	taskName := fmt.Sprintf("Leased by %s using leasing.skia.org", requester)
	taskRequest := &swarming_api.SwarmingRpcsNewTaskRequest{
		Name:     taskName,
		Priority: leaseTaskPriority,
		TaskSlices: []*swarming_api.SwarmingRpcsTaskSlice{
			{
				ExpirationSecs: expirationSecs,
				Properties: &swarming_api.SwarmingRpcsTaskProperties{
					CipdInput:            cipdInput,
					Dimensions:           dims,
					ExecutionTimeoutSecs: executionTimeoutSecs,
					Command:              command,
					IoTimeoutSecs:        ioTimeoutSecs,
				},
			},
		},
		User: "skiabot@google.com",
	}

	casInput, err := swarming.MakeCASReference(casDigest, swarmingInstance.CasInstance)
	if err != nil {
		return "", skerr.Wrapf(err, "Invalid CAS input")
	}
	taskRequest.TaskSlices[0].Properties.CasInputRoot = casInput

	swarmingClient := *GetSwarmingClient(pool)
	resp, err := swarmingClient.TriggerTask(ctx, taskRequest)
	if err != nil {
		return "", fmt.Errorf("Could not trigger swarming task %s", err)
	}
	return resp.TaskId, nil
}

// GetSwarmingTaskLink returns a link to the given Swarming task.
func GetSwarmingTaskLink(server, taskID string) string {
	return fmt.Sprintf("https://%s/task?id=%s", server, taskID)
}

// GetSwarmingBotLink returns a link to the given Swarming bot.
func GetSwarmingBotLink(server, botID string) string {
	return fmt.Sprintf("https://%s/bot?id=%s", server, botID)
}
<commit_msg>[leasing] Use python3 from CIPD<commit_after>/*
	Used by the Leasing Server to interact with swarming.
*/
package main

import (
	"context"
	"fmt"
	"strings"

	swarming_api "go.chromium.org/luci/common/api/swarming/swarming/v1"
	"google.golang.org/api/compute/v1"

	"go.skia.org/infra/go/auth"
	"go.skia.org/infra/go/baseapp"
	"go.skia.org/infra/go/cas"
	"go.skia.org/infra/go/cas/rbe"
	"go.skia.org/infra/go/httputils"
	"go.skia.org/infra/go/skerr"
	"go.skia.org/infra/go/swarming"
	"go.skia.org/infra/leasing/go/types"
)

// SwarmingInstanceClients contains all of the API clients needed to interact
// with a given Swarming instance.
type SwarmingInstanceClients struct {
	SwarmingServer string
	SwarmingClient *swarming.ApiClient

	CasClient   *cas.CAS
	CasInstance string
}

var (
	casClientPublic  cas.CAS
	casClientPrivate cas.CAS

	swarmingClientPublic  swarming.ApiClient
	swarmingClientPrivate swarming.ApiClient

	// PublicSwarming contains the API clients needed for the public Swarming
	// instance.
	PublicSwarming *SwarmingInstanceClients = &SwarmingInstanceClients{
		SwarmingServer: swarming.SWARMING_SERVER,
		SwarmingClient: &swarmingClientPublic,
		CasClient:      &casClientPublic,
		CasInstance:    rbe.InstanceChromiumSwarm,
	}

	// InternalSwarming contains the API clients needed for the internal
	// Swarming instance.
	InternalSwarming *SwarmingInstanceClients = &SwarmingInstanceClients{
		SwarmingServer: swarming.SWARMING_SERVER_PRIVATE,
		SwarmingClient: &swarmingClientPrivate,
		CasClient:      &casClientPrivate,
		CasInstance:    rbe.InstanceChromeSwarming,
	}

	// PoolsToSwarmingInstance maps Swarming pool names to Swarming instances.
	PoolsToSwarmingInstance = map[string]*SwarmingInstanceClients{
		"Skia":             PublicSwarming,
		"SkiaCT":           PublicSwarming,
		"SkiaInternal":     InternalSwarming,
		"CT":               InternalSwarming,
		"CTAndroidBuilder": InternalSwarming,
		"CTLinuxBuilder":   InternalSwarming,
	}

	cpythonPackage = &swarming_api.SwarmingRpcsCipdPackage{
		PackageName: "infra/3pp/tools/cpython3/${platform}",
		Path:        "python",
		Version:     "version:2@3.8.10.chromium.19",
	}
)

// SwarmingInit initializes Swarming globally.
func SwarmingInit(serviceAccountFile string) error {
	ts, err := auth.NewDefaultTokenSource(*baseapp.Local, swarming.AUTH_SCOPE, compute.CloudPlatformScope)
	if err != nil {
		return skerr.Wrapf(err, "Problem setting up default token source")
	}

	// Public CAS client.
	casClientPublic, err = rbe.NewClient(context.TODO(), rbe.InstanceChromiumSwarm, ts)
	if err != nil {
		return skerr.Wrapf(err, "Failed to create RBE client")
	}

	// Private CAS client.
	casClientPrivate, err = rbe.NewClient(context.TODO(), rbe.InstanceChromeSwarming, ts)
	if err != nil {
		return skerr.Wrapf(err, "Failed to create RBE client")
	}

	// Authenticated HTTP client.
	httpClient := httputils.DefaultClientConfig().WithTokenSource(ts).With2xxOnly().Client()

	// Public Swarming API client.
	swarmingClientPublic, err = swarming.NewApiClient(httpClient, swarming.SWARMING_SERVER)
	if err != nil {
		return skerr.Wrapf(err, "Failed to create public swarming client")
	}
	// Private Swarming API client.
	swarmingClientPrivate, err = swarming.NewApiClient(httpClient, swarming.SWARMING_SERVER_PRIVATE)
	if err != nil {
		return skerr.Wrapf(err, "Failed to create private swarming client")
	}

	return nil
}

// GetSwarmingInstance returns the Swarming instance for the given Swarming
// pool.
func GetSwarmingInstance(pool string) *SwarmingInstanceClients {
	return PoolsToSwarmingInstance[pool]
}

// GetSwarmingClient returns the Swarming client for the given Swarming pool.
func GetSwarmingClient(pool string) *swarming.ApiClient {
	return GetSwarmingInstance(pool).SwarmingClient
}

// GetCASClient returns the CAS client for the given Swarming pool.
func GetCASClient(pool string) *cas.CAS {
	return GetSwarmingInstance(pool).CasClient
}

func getPoolDetails(ctx context.Context, pool string) (*types.PoolDetails, error) {
	swarmingClient := *GetSwarmingClient(pool)
	bots, err := swarmingClient.ListBotsForPool(ctx, pool)
	if err != nil {
		return nil, fmt.Errorf("Could not list bots in pool: %s", err)
	}
	osTypes := map[string]int{}
	osToDeviceTypes := map[string]map[string]int{}
	for _, bot := range bots {
		if bot.IsDead || bot.Quarantined {
			// Do not include dead/quarantined bots in the counts below.
			continue
		}
		osType := ""
		deviceType := ""
		for _, d := range bot.Dimensions {
			if d.Key == "os" {
				val := ""
				// Use the longest string from the os values because that is what the swarming UI
				// does and it works in all cases we have (atleast as of 11/1/17).
				for _, v := range d.Value {
					if len(v) > len(val) {
						val = v
					}
				}
				osType = val
			}
			if d.Key == "device_type" {
				// There should only be one value for device type.
				deviceType = d.Value[0]
			}
		}
		osTypes[osType]++
		if _, ok := osToDeviceTypes[osType]; !ok {
			osToDeviceTypes[osType] = map[string]int{}
		}
		if deviceType != "" {
			osToDeviceTypes[osType][deviceType]++
		}
	}
	return &types.PoolDetails{
		OsTypes:         osTypes,
		OsToDeviceTypes: osToDeviceTypes,
	}, nil
}

// GetDetailsOfAllPools returns details for each of the known Swarming pools.
func GetDetailsOfAllPools(ctx context.Context) (map[string]*types.PoolDetails, error) {
	poolToDetails := map[string]*types.PoolDetails{}
	for pool := range PoolsToSwarmingInstance {
		details, err := getPoolDetails(ctx, pool)
		if err != nil {
			return nil, err
		}
		poolToDetails[pool] = details
	}
	return poolToDetails, nil
}

// AddLeasingArtifactsToCAS uploads the leasing artifacts and merges them into
// the given CAS input if it exists.
func AddLeasingArtifactsToCAS(ctx context.Context, pool string, casInput *swarming_api.SwarmingRpcsCASReference) (string, error) {
	client := *GetCASClient(pool)

	// Upload the leasing artifacts.
	// TODO(rmistry): After this has been done once, we should be able to just
	// use the digest as a constant.
	leasingScriptDigest, err := client.Upload(ctx, *artifactsDir, []string{"leasing.py"}, nil)
	if err != nil {
		return "", skerr.Wrap(err)
	}
	if casInput == nil {
		return leasingScriptDigest, nil
	}
	return client.Merge(ctx, []string{leasingScriptDigest, rbe.DigestToString(casInput.Digest.Hash, casInput.Digest.SizeBytes)})
}

// GetSwarmingTask retrieves the given Swarming task.
func GetSwarmingTask(ctx context.Context, pool, taskID string) (*swarming_api.SwarmingRpcsTaskResult, error) {
	swarmingClient := *GetSwarmingClient(pool)
	return swarmingClient.GetTask(ctx, taskID, false)
}

// GetSwarmingTaskMetadata returns the metadata for the given Swarming task.
func GetSwarmingTaskMetadata(ctx context.Context, pool, taskID string) (*swarming_api.SwarmingRpcsTaskRequestMetadata, error) {
	swarmingClient := *GetSwarmingClient(pool)
	return swarmingClient.GetTaskMetadata(ctx, taskID)
}

// IsBotIDValid returns true iff the given bot exists in the given pool.
func IsBotIDValid(ctx context.Context, pool, botID string) (bool, error) {
	swarmingClient := *GetSwarmingClient(pool)
	dims := map[string]string{
		"pool": pool,
		"id":   botID,
	}
	bots, err := swarmingClient.ListBots(ctx, dims)
	if err != nil {
		return false, fmt.Errorf("Could not query swarming bots with %s: %s", dims, err)
	}
	if len(bots) > 1 {
		return false, fmt.Errorf("Something went wrong, more than 1 bot was returned with %s: %s", dims, err)
	}
	if len(bots) == 0 {
		// There were no matches for the pool + botId combination.
		return false, nil
	}
	if bots[0].BotId == botID {
		return true, nil
	}
	return false, fmt.Errorf("%s returned %s instead of the expected %s", dims, bots[1].BotId, botID)
}

// TriggerSwarmingTask triggers the given Swarming task.
func TriggerSwarmingTask(ctx context.Context, pool, requester, datastoreID, osType, deviceType, botID, serverURL, casDigest, relativeCwd string, cipdInput *swarming_api.SwarmingRpcsCipdInput, cmd []string) (string, error) {
	dimsMap := map[string]string{
		"pool": pool,
	}
	if osType != "" {
		dimsMap["os"] = osType
	}
	if deviceType != "" {
		dimsMap["device_type"] = deviceType
	}
	if botID != "" {
		dimsMap["id"] = botID
	}
	dims := make([]*swarming_api.SwarmingRpcsStringPair, 0, len(dimsMap))
	for k, v := range dimsMap {
		dims = append(dims, &swarming_api.SwarmingRpcsStringPair{
			Key:   k,
			Value: v,
		})
	}

	// Always include cpython for Windows. See skbug.com/9501 for context and
	// for why we do not include it for all architectures.
	pythonBinary := "python/bin/python3"
	if cipdInput == nil {
		cipdInput = &swarming_api.SwarmingRpcsCipdInput{}
	}
	if cipdInput.Packages == nil {
		cipdInput.Packages = []*swarming_api.SwarmingRpcsCipdPackage{cpythonPackage}
	} else {
		cipdInput.Packages = append(cipdInput.Packages, cpythonPackage)
	}

	// Arguments that will be passed to leasing.py
	extraArgs := []string{
		"--task-id", datastoreID,
		"--os-type", osType,
		"--leasing-server", serverURL,
		"--debug-command", strings.Join(cmd, " "),
		"--command-relative-dir", relativeCwd,
	}

	// Construct the command.
	command := []string{pythonBinary, "leasing.py"}
	command = append(command, extraArgs...)

	swarmingInstance := GetSwarmingInstance(pool)
	expirationSecs := int64(swarming.RECOMMENDED_EXPIRATION.Seconds())
	executionTimeoutSecs := int64(swarmingHardTimeout.Seconds())
	ioTimeoutSecs := int64(swarmingHardTimeout.Seconds())
	taskName := fmt.Sprintf("Leased by %s using leasing.skia.org", requester)
	taskRequest := &swarming_api.SwarmingRpcsNewTaskRequest{
		Name:     taskName,
		Priority: leaseTaskPriority,
		TaskSlices: []*swarming_api.SwarmingRpcsTaskSlice{
			{
				ExpirationSecs: expirationSecs,
				Properties: &swarming_api.SwarmingRpcsTaskProperties{
					CipdInput:            cipdInput,
					Dimensions:           dims,
					ExecutionTimeoutSecs: executionTimeoutSecs,
					Command:              command,
					IoTimeoutSecs:        ioTimeoutSecs,
				},
			},
		},
		User: "skiabot@google.com",
	}

	casInput, err := swarming.MakeCASReference(casDigest, swarmingInstance.CasInstance)
	if err != nil {
		return "", skerr.Wrapf(err, "Invalid CAS input")
	}
	taskRequest.TaskSlices[0].Properties.CasInputRoot = casInput

	swarmingClient := *GetSwarmingClient(pool)
	resp, err := swarmingClient.TriggerTask(ctx, taskRequest)
	if err != nil {
		return "", fmt.Errorf("Could not trigger swarming task %s", err)
	}
	return resp.TaskId, nil
}

// GetSwarmingTaskLink returns a link to the given Swarming task.
func GetSwarmingTaskLink(server, taskID string) string {
	return fmt.Sprintf("https://%s/task?id=%s", server, taskID)
}

// GetSwarmingBotLink returns a link to the given Swarming bot.
func GetSwarmingBotLink(server, botID string) string {
	return fmt.Sprintf("https://%s/bot?id=%s", server, botID)
}
<|endoftext|>


<commit_before>package sqlf_test

import (
	"database/sql"
	"fmt"
	"log"

	"github.com/jjeffery/sqlf/sqlf"
)

func openTestDB() *sql.DB {
	db, err := sql.Open("sqlite3", ":memory:")
	if err != nil {
		log.Fatal(err)
	}
	_, err = db.Exec(`
		create table users(
			id integer primary key autoincrement,
			given_name text,
			family_name text
		)
	`)
	if err != nil {
		log.Fatal(err)
	}
	_, err = db.Exec(`
		insert into users(given_name, family_name)
		values('John', 'Citizen')
	`)
	if err != nil {
		log.Fatal(err)
	}
	return db
}

func ExampleInsertRowStmt() {
	type User struct {
		ID         int64 `sql:",primary key auto increment"`
		GivenName  string
		FamilyName string
	}

	stmt := sqlf.NewInsertRowStmt(User{}, `users`)
	fmt.Println(stmt.String())

	var db *sql.DB = openTestDB()

	// Get user with specified primary key
	u := &User{GivenName: "Jane", FamilyName: "Doe"}
	err := stmt.Exec(db, u)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Inserted row: ID=%d\n", u.ID)

	// Output:
	// insert into users(`given_name`,`family_name`) values(?,?)
	// Inserted row: ID=2
}

func ExampleGetRowStmt() {
	type User struct {
		ID         int64 `sql:",primary key auto increment"`
		GivenName  string
		FamilyName string
	}

	stmt := sqlf.NewGetRowStmt(User{}, `users`)
	fmt.Println(stmt.String())

	var db *sql.DB = openTestDB()

	// Get user with specified primary key
	u := &User{ID: 1}
	_, err := stmt.Get(db, u)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("ID=%d, GivenName=%q, FamilyName=%q\n",
		u.ID, u.GivenName, u.FamilyName)

	// Output:
	// select `id`,`given_name`,`family_name` from users where `id`=?
	// ID=1, GivenName="John", FamilyName="Citizen"
}

func ExampleExecRowStmt() {
	type User struct {
		ID         int64 `sql:",primary key auto increment"`
		GivenName  string
		FamilyName string
	}

	updateStmt := sqlf.NewUpdateRowStmt(User{}, `users`)
	deleteStmt := sqlf.NewDeleteRowStmt(User{}, `users`)
	fmt.Println(updateStmt.String())
	fmt.Println(deleteStmt.String())

	var db *sql.DB = openTestDB()

	// Get user with specified primary key
	u := &User{ID: 1}
	_, err := sqlf.NewGetRowStmt(User{}, `users`).Get(db, u)
	if err != nil {
		log.Fatal(err)
	}

	// Update row
	u.GivenName = "Donald"
	n, err := updateStmt.Exec(db, u)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("number of rows updated:", n)

	// Delete row
	n, err = deleteStmt.Exec(db, u)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("number of rows deleted:", n)

	// Output:
	// update users set `given_name`=?,`family_name`=? where `id`=?
	// delete from users where `id`=?
	// number of rows updated: 1
	// number of rows deleted: 1
}

func ExampleNewDeleteRowStmt() {
	type User struct {
		ID         int64 `sql:",primary key auto increment"`
		GivenName  string
		FamilyName string
	}

	stmt := sqlf.NewDeleteRowStmt(User{}, `users`)
	fmt.Println(stmt.String())

	// creates a row with ID=1
	var db *sql.DB = openTestDB()

	// Delete user with specified primary key
	u := &User{ID: 1}
	n, err := stmt.Exec(db, u)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("number of rows deleted:", n)

	// Output:
	// delete from users where `id`=?
	// number of rows deleted: 1
}

func ExampleupdateRowStmt() {
	type User struct {
		ID         int64 `sql:",primary key auto increment"`
		GivenName  string
		FamilyName string
	}

	updateStmt := sqlf.NewUpdateRowStmt(User{}, `users`)
	fmt.Println(updateStmt.String())

	var db *sql.DB = openTestDB()

	// Get user with specified primary key
	u := &User{ID: 1}
	_, err := sqlf.NewGetRowStmt(User{}, `users`).Get(db, u)
	if err != nil {
		log.Fatal(err)
	}

	// Update row
	u.GivenName = "Donald"
	n, err := updateStmt.Exec(db, u)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("number of rows updated:", n)

	// Output:
	// update users set `given_name`=?,`family_name`=? where `id`=?
	// number of rows updated: 1
}

func ExampleSelectStmt() {
	type User struct {
		ID      int64 `sql:",primary key auto increment"`
		Login   string
		HashPwd string
		Name    string
	}

	stmt := sqlf.NewSelectStmt(User{}, `
		select distinct {alias u}
		from users u
		inner join user_search_terms t on t.user_id = u.id
		where t.search_term like ?
	`)
	fmt.Println(stmt.String())

	// Output:
	// select distinct u.`id`,u.`login`,u.`hash_pwd`,u.`name` from users u inner join user_search_terms t on t.user_id = u.id where t.search_term like ?
}
<commit_msg>Update examples<commit_after>package sqlf_test

import (
	"database/sql"
	"fmt"
	"log"

	"github.com/jjeffery/sqlf/sqlf"
)

func openTestDB() *sql.DB {
	db, err := sql.Open("sqlite3", ":memory:")
	if err != nil {
		log.Fatal(err)
	}
	_, err = db.Exec(`
		create table users(
			id integer primary key autoincrement,
			given_name text,
			family_name text
		)
	`)
	if err != nil {
		log.Fatal(err)
	}
	_, err = db.Exec(`
		insert into users(given_name, family_name)
		values('John', 'Citizen')
	`)
	if err != nil {
		log.Fatal(err)
	}
	return db
}

func ExampleInsertRowStmt() {
	type User struct {
		ID         int64 `sql:",primary key auto increment"`
		GivenName  string
		FamilyName string
	}

	stmt := sqlf.NewInsertRowStmt(User{}, `users`)
	fmt.Println(stmt.String())

	var db *sql.DB = openTestDB()

	// Get user with specified primary key
	u := &User{GivenName: "Jane", FamilyName: "Doe"}
	err := stmt.Exec(db, u)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Inserted row: ID=%d\n", u.ID)

	// Output:
	// insert into users(`given_name`,`family_name`) values(?,?)
	// Inserted row: ID=2
}

func ExampleGetRowStmt() {
	type User struct {
		ID         int64 `sql:",primary key auto increment"`
		GivenName  string
		FamilyName string
	}

	stmt := sqlf.NewGetRowStmt(User{}, `users`)
	fmt.Println(stmt.String())

	var db *sql.DB = openTestDB()

	// Get user with specified primary key
	u := &User{ID: 1}
	_, err := stmt.Get(db, u)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("ID=%d, GivenName=%q, FamilyName=%q\n",
		u.ID, u.GivenName, u.FamilyName)

	// Output:
	// select `id`,`given_name`,`family_name` from users where `id`=?
	// ID=1, GivenName="John", FamilyName="Citizen"
}

func ExampleExecRowStmt() {
	type User struct {
		ID         int64 `sql:",primary key auto increment"`
		GivenName  string
		FamilyName string
	}

	updateStmt := sqlf.NewUpdateRowStmt(User{}, `users`)
	deleteStmt := sqlf.NewDeleteRowStmt(User{}, `users`)
	fmt.Println(updateStmt.String())
	fmt.Println(deleteStmt.String())

	var db *sql.DB = openTestDB()

	// Get user with specified primary key
	u := &User{ID: 1}
	_, err := sqlf.NewGetRowStmt(User{}, `users`).Get(db, u)
	if err != nil {
		log.Fatal(err)
	}

	// Update row
	u.GivenName = "Donald"
	n, err := updateStmt.Exec(db, u)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("number of rows updated:", n)

	// Delete row
	n, err = deleteStmt.Exec(db, u)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("number of rows deleted:", n)

	// Output:
	// update users set `given_name`=?,`family_name`=? where `id`=?
	// delete from users where `id`=?
	// number of rows updated: 1
	// number of rows deleted: 1
}

func ExampleNewDeleteRowStmt() {
	type User struct {
		ID         int64 `sql:",primary key auto increment"`
		GivenName  string
		FamilyName string
	}

	stmt := sqlf.NewDeleteRowStmt(User{}, `users`)
	fmt.Println(stmt.String())

	// creates a row with ID=1
	var db *sql.DB = openTestDB()

	// Delete user with specified primary key
	u := &User{ID: 1}
	n, err := stmt.Exec(db, u)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("number of rows deleted:", n)

	// Output:
	// delete from users where `id`=?
	// number of rows deleted: 1
}

func ExampleNewUpdateRowStmt() {
	type User struct {
		ID         int64 `sql:",primary key auto increment"`
		GivenName  string
		FamilyName string
	}

	updateStmt := sqlf.NewUpdateRowStmt(User{}, `users`)
	fmt.Println(updateStmt.String())

	var db *sql.DB = openTestDB()

	// Get user with specified primary key
	u := &User{ID: 1}
	_, err := sqlf.NewGetRowStmt(User{}, `users`).Get(db, u)
	if err != nil {
		log.Fatal(err)
	}

	// Update row
	u.GivenName = "Donald"
	n, err := updateStmt.Exec(db, u)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("number of rows updated:", n)

	// Output:
	// update users set `given_name`=?,`family_name`=? where `id`=?
	// number of rows updated: 1
}

func ExampleSelectStmt() {
	type User struct {
		ID      int64 `sql:",primary key auto increment"`
		Login   string
		HashPwd string
		Name    string
	}

	stmt := sqlf.NewSelectStmt(User{}, `
		select distinct {alias u}
		from users u
		inner join user_search_terms t on t.user_id = u.id
		where t.search_term like ?
	`)
	fmt.Println(stmt.String())

	// Output:
	// select distinct u.`id`,u.`login`,u.`hash_pwd`,u.`name` from users u inner join user_search_terms t on t.user_id = u.id where t.search_term like ?
}
<|endoftext|>


<commit_before>package configs

import (
	"fmt"
	"github.com/vladpereskokov/Technopark_HighLoad-nginx/src/utils"
)

type Dir struct {
	Path string `json:"path"`
}

func (dir *Dir) findDir() {
	if dir.Path == "pwd" {
		dir.Path = utils.Pwd()
	}
}

func (config *Config) GetDir() string {
	config.Dir.findDir()

	fmt.Println(config.Dir.Path)

	return config.Dir.Path
}
<commit_msg>move methods dir config to dir model<commit_after>package configs

import (
	"fmt"
	"github.com/vladpereskokov/Technopark_HighLoad-nginx/src/utils"
)

type Dir struct {
	Path string `json:"path"`
}

func (dir *Dir) findDir() {
	if dir.Path == "pwd" {
		dir.Path = utils.Pwd()
	}
}

func (dir *Dir) GetDir() string {
	dir.findDir()

	fmt.Println(dir.Path)

	return dir.Path
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
)

func main() {
	help := flag.NewFlagSet("help", flag.ExitOnError)
	verbose := help.Bool("v", false, "詳しく")
	moreVerbose := help.Bool("vv", false, "より詳しく")
	mostVerbose := help.Bool("vvv", false, "最も詳しく") // FIXME: 4つ以上vが続いたら3つで処理したい

	money := flag.NewFlagSet("money", flag.ExitOnError)

	if len(os.Args) == 1 {
		fmt.Println("usage: argparse <command> [<args>]")
		fmt.Println("\thelp: ヘルプをプリントします")
		fmt.Println("\tmoney: 為替レートを調べます")
		return
	}

	switch os.Args[1] {
	case "help":
		help.Parse(os.Args[2:])
	case "money":
		money.Parse(os.Args[2:])
	default:
		fmt.Printf("%q is not valid command.\n", os.Args[1])
		os.Exit(2)
	}

	if help.Parsed() {
		message := "Help me."
		if *verbose {
			message = "Help me!"
		}
		if *moreVerbose {
			message = "Help me!!"
		}
		if *mostVerbose {
			message = "HELP ME!!"
		}
		fmt.Println(message)
	}

	if money.Parsed() {
		if len(money.Args()) < 2 {
			// e.g. $ <cmd> money USD
			fmt.Println("エラー: 通貨をふたつ入力してください")
			return
		}

		from, to := money.Arg(0), money.Arg(1)
		if from == "" || to == "" {
			// e.g. $ <cmd> money USD ''
			fmt.Println("エラー: 通貨をふたつ入力してください")
			return
		}
		fmt.Printf("%v/%v: 100円/$\n", from, to) // 適当
	}
}
<commit_msg>verbosityの解析でエラーを出さないよう直した<commit_after>package main

import (
	"flag"
	"fmt"
	"math"
	"os"
	"regexp"
	"strconv"
	"strings"
)

var regex = regexp.MustCompile("^-v+$")

// 引数からメッセージ詳細度を算出する。算出に使わなかった引数は戻り値のスライスに返却する。
func Verbosity(xs []string) (int, []string) {
	var v float64
	others := make([]string, 0, len(xs))
	for _, x := range xs {
		if regex.MatchString(x) {
			v = math.Max(v, float64(strings.Count(x, "v")))
		} else {
			others = append(others, x)
		}
	}
	return int(v), others
}

func main() {
	help := flag.NewFlagSet("help", flag.ExitOnError)
	var verbosity int
	help.IntVar(&verbosity, "verbosity", 0, "")

	money := flag.NewFlagSet("money", flag.ExitOnError)
	if len(os.Args) == 1 {
		fmt.Println("usage: argparse <command> [<args>]")
		fmt.Println("\thelp: ヘルプをプリントします")
		fmt.Println("\tmoney: 為替レートを調べます")
		return
	}

	switch os.Args[1] {
	case "help":
		v, others := Verbosity(os.Args[2:])
		help.Parse(others)
		if verbosity < v {
			// e.g. <cmd> help -verbosity=0 -vvv
			help.Set("verbosity", strconv.Itoa(v))
		}
	case "money":
		money.Parse(os.Args[2:])
	default:
		fmt.Printf("%q is not valid command.\n", os.Args[1])
		os.Exit(2)
	}

	if help.Parsed() {
		message := "Help me."
		if verbosity == 1 {
			message = "Help me!"
		}
		if verbosity == 2 {
			message = "Help me!!"
		}
		if verbosity >= 3 {
			message = "HELP ME!!"
		}
		fmt.Println(message)
	}

	if money.Parsed() {
		if len(money.Args()) < 2 {
			// e.g. $ <cmd> money USD
			fmt.Println("エラー: 通貨をふたつ入力してください")
			return
		}

		from, to := money.Arg(0), money.Arg(1)
		if from == "" || to == "" {
			// e.g. $ <cmd> money USD ''
			fmt.Println("エラー: 通貨をふたつ入力してください")
			return
		}
		fmt.Printf("%v/%v: 100円/$\n", from, to) // 適当
	}
}
<|endoftext|>


<commit_before>/*
 * // Copyright (c) 2017 Cisco and/or its affiliates.
 * //
 * // 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 cache

import (
	"github.com/ligato/cn-infra/datasync"

	nsmodel "github.com/contiv/vpp/plugins/ksr/model/namespace"
	podmodel "github.com/contiv/vpp/plugins/ksr/model/pod"
	policymodel "github.com/contiv/vpp/plugins/ksr/model/policy"
)

// PolicyCacheAPI defines API of PolicyCache used for a non-persistent storage
// of K8s State data with fast lookups.
// The cache processes K8s State data updates and RESYNC events through Update()
// and Resync() APIs, respectively.
// The cache allows to get notified about changes via convenient callbacks.
// A watcher needs to implement the interface PolicyCacheWatcher and subscribe
// for watching using Watch() API.
// The cache provides various fast lookup methods (e.g. by the label selector).
type PolicyCacheAPI interface {
	// Update processes a datasync change event associated with K8s State data.
	// The change is applied into the cache and all subscribed watchers are
	// notified.
	// The function will forward any error returned by a watcher.
	Update(dataChngEv datasync.ChangeEvent) error

	// Resync processes a datasync resync event associated with K8s State data.
	// The cache content is full replaced with the received data and all
	// subscribed watchers are notified.
	// The function will forward any error returned by a watcher.
	Resync(resyncEv datasync.ResyncEvent) error

	// Watch subscribes a new watcher.
	Watch(watcher PolicyCacheWatcher) error

	// LookupPod returns data of a given Pod.
	LookupPod(pod podmodel.ID) (found bool, data *podmodel.Pod)

	// LookupPodsByNsLabelSelector evaluates label selector (expression and/or match
	// labels) and returns IDs of matching pods in a namespace.
	LookupPodsByNsLabelSelector(podLabelSelector *policymodel.Policy_LabelSelector) (pods []podmodel.ID)

	// LookupPodsByLabelSelectorInsideNs evaluates label selector (expression and/or match
	// labels and returns IDs of matching pods.
	LookupPodsByLabelSelectorInsideNs(policyNamespace string, podLabelSelector *policymodel.Policy_LabelSelector) (pods []podmodel.ID)

	// LookupPodsByNamespace returns IDs of all pods inside a given namespace.
	LookupPodsByNamespace(policyNamespace string) (pods []podmodel.ID)

	// ListAllPods returns IDs of all known pods.
	ListAllPods() (pods []podmodel.ID)

	// LookupPolicy returns data of a given Policy.
	LookupPolicy(policy policymodel.ID) (found bool, data *policymodel.Policy)

	// LookupPoliciesByPod returns IDs of all policies assigned to a given pod.
	LookupPoliciesByPod(pod podmodel.ID) (policies []policymodel.ID)

	// ListAllPolicies returns IDs of all policies.
	ListAllPolicies() (policies []policymodel.ID)

	// LookupNamespace returns data of a given namespace.
	LookupNamespace(namespace nsmodel.ID) (found bool, data *nsmodel.Namespace)

	// ListAllNamespaces returns IDs of all known namespaces.
	ListAllNamespaces() (namespaces []nsmodel.ID)
}

// PolicyCacheWatcher defines interface that a PolicyCache watcher must implement.
type PolicyCacheWatcher interface {
	// Resync is called by Policy Cache during a RESYNC event.
	Resync(data *DataResyncEvent) error

	// AddPod is called by Policy Cache when a new pod is created.
	AddPod(podID podmodel.ID, pod *podmodel.Pod) error

	// DelPod is called by Policy Cache after a pod was removed.
	DelPod(podID podmodel.ID, pod *podmodel.Pod) error

	// UpdatePod is called by Policy Cache when data of a pod were modified.
	UpdatePod(podID podmodel.ID, oldPod, newPod *podmodel.Pod) error

	// AddPolicy is called by Policy Cache when a new policy is created.
	AddPolicy(policy *policymodel.Policy) error

	// DelPolicy is called by Policy Cache after a policy was removed.
	DelPolicy(policy *policymodel.Policy) error

	// UpdatePolicy is called by Policy Cache when date of a policy were
	// modified.
	UpdatePolicy(oldPolicy, newPolicy *policymodel.Policy) error

	// AddNamespace is called by Policy Cache when a new namespace is created.
	AddNamespace(ns *nsmodel.Namespace) error

	// DelNamespace is called by Policy Cache after a namespace was removed.
	DelNamespace(ns *nsmodel.Namespace) error

	// UpdateNamespace is called by Policy Cache when data of a namespace were
	// modified.
	UpdateNamespace(oldNs, newNs *nsmodel.Namespace) error
}
<commit_msg>Fix function comments.<commit_after>/*
 * // Copyright (c) 2017 Cisco and/or its affiliates.
 * //
 * // 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 cache

import (
	"github.com/ligato/cn-infra/datasync"

	nsmodel "github.com/contiv/vpp/plugins/ksr/model/namespace"
	podmodel "github.com/contiv/vpp/plugins/ksr/model/pod"
	policymodel "github.com/contiv/vpp/plugins/ksr/model/policy"
)

// PolicyCacheAPI defines API of PolicyCache used for a non-persistent storage
// of K8s State data with fast lookups.
// The cache processes K8s State data updates and RESYNC events through Update()
// and Resync() APIs, respectively.
// The cache allows to get notified about changes via convenient callbacks.
// A watcher needs to implement the interface PolicyCacheWatcher and subscribe
// for watching using Watch() API.
// The cache provides various fast lookup methods (e.g. by the label selector).
type PolicyCacheAPI interface {
	// Update processes a datasync change event associated with K8s State data.
	// The change is applied into the cache and all subscribed watchers are
	// notified.
	// The function will forward any error returned by a watcher.
	Update(dataChngEv datasync.ChangeEvent) error

	// Resync processes a datasync resync event associated with K8s State data.
	// The cache content is full replaced with the received data and all
	// subscribed watchers are notified.
	// The function will forward any error returned by a watcher.
	Resync(resyncEv datasync.ResyncEvent) error

	// Watch subscribes a new watcher.
	Watch(watcher PolicyCacheWatcher) error

	// LookupPod returns data of a given Pod.
	LookupPod(pod podmodel.ID) (found bool, data *podmodel.Pod)

	// LookupPodsByNsLabelSelector evaluates namespace label selector (expression and/or match
	// labels) and returns IDs of pods in the matched namespaces.
	LookupPodsByNsLabelSelector(podLabelSelector *policymodel.Policy_LabelSelector) (pods []podmodel.ID)

	// LookupPodsByLabelSelectorInsideNs evaluates pod label selector (expression and/or match
	// labels) in a namespace and returns IDs of matching pods.
	LookupPodsByLabelSelectorInsideNs(policyNamespace string, podLabelSelector *policymodel.Policy_LabelSelector) (pods []podmodel.ID)

	// LookupPodsByNamespace returns IDs of all pods inside a given namespace.
	LookupPodsByNamespace(policyNamespace string) (pods []podmodel.ID)

	// ListAllPods returns IDs of all known pods.
	ListAllPods() (pods []podmodel.ID)

	// LookupPolicy returns data of a given Policy.
	LookupPolicy(policy policymodel.ID) (found bool, data *policymodel.Policy)

	// LookupPoliciesByPod returns IDs of all policies assigned to a given pod.
	LookupPoliciesByPod(pod podmodel.ID) (policies []policymodel.ID)

	// ListAllPolicies returns IDs of all policies.
	ListAllPolicies() (policies []policymodel.ID)

	// LookupNamespace returns data of a given namespace.
	LookupNamespace(namespace nsmodel.ID) (found bool, data *nsmodel.Namespace)

	// ListAllNamespaces returns IDs of all known namespaces.
	ListAllNamespaces() (namespaces []nsmodel.ID)
}

// PolicyCacheWatcher defines interface that a PolicyCache watcher must implement.
type PolicyCacheWatcher interface {
	// Resync is called by Policy Cache during a RESYNC event.
	Resync(data *DataResyncEvent) error

	// AddPod is called by Policy Cache when a new pod is created.
	AddPod(podID podmodel.ID, pod *podmodel.Pod) error

	// DelPod is called by Policy Cache after a pod was removed.
	DelPod(podID podmodel.ID, pod *podmodel.Pod) error

	// UpdatePod is called by Policy Cache when data of a pod were modified.
	UpdatePod(podID podmodel.ID, oldPod, newPod *podmodel.Pod) error

	// AddPolicy is called by Policy Cache when a new policy is created.
	AddPolicy(policy *policymodel.Policy) error

	// DelPolicy is called by Policy Cache after a policy was removed.
	DelPolicy(policy *policymodel.Policy) error

	// UpdatePolicy is called by Policy Cache when date of a policy were
	// modified.
	UpdatePolicy(oldPolicy, newPolicy *policymodel.Policy) error

	// AddNamespace is called by Policy Cache when a new namespace is created.
	AddNamespace(ns *nsmodel.Namespace) error

	// DelNamespace is called by Policy Cache after a namespace was removed.
	DelNamespace(ns *nsmodel.Namespace) error

	// UpdateNamespace is called by Policy Cache when data of a namespace were
	// modified.
	UpdateNamespace(oldNs, newNs *nsmodel.Namespace) error
}
<|endoftext|>


<commit_before>package db

import (
	"config"
	"errrs"
	"fmt"
	"github.com/coopernurse/gorp"
	log "logger"
	"os"
	osuser "os/user"
	"path"
	"path/filepath"
	"strings"
	"taglib"
)

type entry struct {
	folder string
	file   string
}

const bufferSize = 128

type updater struct {
	tx           chan *gorp.Transaction
	allFiles     chan entry // all files seen
	newFiles     chan entry // files not yet in db
	importFiles  chan entry // files to import
	success      chan bool
	stopStepping chan bool

	// the receiving goroutine shall increment these
	numAllFiles      int
	numNewFiles      int
	numImportFiles   int
	numInvalidFiles  int
	numFailedFiles   int
	numImportedFiles int
}

var IgnoredTypes = []string{
	"jpg", "jpeg", "png", "gif", "nfo", "m3u", "log", "sfv", "txt", "cue",
	"itc2", "html", "xml", "ipa", "asd", "plist", "itdb", "itl", "tmp", "ini",
	"sh", "sha1", "blb"}

func (d *DB) Update() {
	// keep file base up to date
	searchPath := config.Current.MediaPath
	if strings.Contains(searchPath, "~") {
		user, err := osuser.Current()
		if err != nil {
			log.Log.Println("Error getting user home directory:", err)
			return
		}
		searchPath = strings.Replace(searchPath, "~", user.HomeDir, -1)
	}
	if _, err := os.Stat(searchPath); os.IsNotExist(err) {
		log.Log.Println("Error: Music path", searchPath, "does not exist!")
		return
	}
	tx, err := d.dbmap.Begin()
	if err != nil {
		log.Log.Println("Could not start db transaction")
		return
	}
	up := &updater{tx: make(chan *gorp.Transaction, 1),
		allFiles:     make(chan entry, bufferSize),
		newFiles:     make(chan entry, bufferSize),
		importFiles:  make(chan entry, bufferSize),
		success:      make(chan bool),
		stopStepping: make(chan bool, 1)}
	up.tx <- tx

	go func() {
		err := filepath.Walk(searchPath, up.step)
		if err != nil {
			log.Log.Println("Updater error:", err)
		}
		close(up.allFiles)
	}()

	go func(input, output chan entry) {
		for entry := range input {
			up.numAllFiles++
			//fmt.Println("suffix filter gets:", entry)
			do := true
			for _, v := range IgnoredTypes {
				if strings.HasSuffix(entry.file, v) {
					//TODO do something with the cover jpgs
					do = false
					break
				}
			}
			if do {
				output <- entry
			}
		}
		close(output)
	}(up.allFiles, up.importFiles)

	go func(input, output chan entry) {
		for entry := range input {
			up.numImportFiles++
			//fmt.Println("seen filter gets:", entry)
			// check if we already did this one
			itemPath := ItemPathView{}
			tx := <-up.tx
			if err := tx.SelectOne(&itemPath,
				`select item_id Id, filename Filename, path Path
				from `+ItemTable+`
				join `+FolderTable+` on `+FolderTable+`.folder_id = `+ItemTable+`.folder_id
				where filename = ?
				and path = ?`,
				entry.file, entry.folder); err != nil {
				fmt.Println("sql error:", err)
				up.success <- false
			}
			up.tx <- tx
			if itemPath.Id != 0 {
				// this one is already in the db
				//TODO check if the tags have changed anyway
				//log.Log.Println("skipping", path)
			} else {
				output <- entry
			}
		}
		close(output)
	}(up.importFiles, up.newFiles)

	go func(input chan entry, success chan bool) {
		for entry := range input {
			up.numNewFiles++
			err := up.analyze(path.Join(entry.folder, entry.file),
				entry.folder, entry.file)
			if err != nil {
				up.numFailedFiles++
				fmt.Println("import error: ", err)
			}
		}
		up.success <- true
		close(up.success)
	}(up.newFiles, up.success)

	success := true
	for v := range up.success {
		if !v {
			up.stopStepping <- true
			success = false
			break
			if err = tx.Rollback(); err != nil {
				log.Log.Println("rollback error:", err)
			}
		}
	}
	if success {
		tx := <-up.tx
		if err = tx.Commit(); err != nil {
			log.Log.Println("Updater error:", err)
		}
	}
	log.Log.Println("Filebase updated:\n",
		"Total Files:        ", up.numAllFiles,
		"\nNon-ignored Files:", up.numImportFiles,
		"\nNew Files:        ", up.numNewFiles,
		"\nImported Files:   ", up.numImportedFiles,
		"\nInvalid/Non-media:", up.numInvalidFiles,
		"\nFailed Files:     ", up.numFailedFiles)
}

func (up *updater) step(file string, info os.FileInfo, err error) error {
	if info == nil ||
		info.Name() == "." ||
		info.Name() == ".." {
		return nil
	}
	if info.IsDir() {
		//log.Log.Println("in", file)
	} else if linked, err := filepath.EvalSymlinks(file); err != nil || file != linked {
		if err != nil {
			log.Log.Println("Error walking files:", err.Error())
			return nil
		}
		filepath.Walk(linked, up.step)
	} else {
		select {
		case <-up.stopStepping:
			return errrs.New("aborting")
		case up.allFiles <- entry{path.Dir(file), info.Name()}:
		}
	}
	return nil
}

func (up *updater) analyze(path string, parent string, file string) error {
	//log.Log.Println("doing", path)

	tag, err := taglib.Read(path)
	if err != nil {
		log.Log.Println("error reading file", path, "-", err)
		up.numInvalidFiles++
		return nil
	}

	defer tag.Close()

	title := tag.Title()
	artist := tag.Artist()
	if title == nil || artist == nil {
		return errrs.New("Title and Artist cannot be nil. File " + path)
	}
	item := &Item{
		Title:       *title,
		Artist:      *artist,
		AlbumArtist: nil,
		Album:       tag.Album(),
		Genre:       tag.Genre(),
		TrackNumber: uint32(tag.Track()),
		Folder:      &Folder{Path: parent},
		Filename:    &file,
	}
	//TODO get album, check ID etc

	tx := <-up.tx
	err = tx.Insert(item)
	up.tx <- tx
	if err != nil {
		log.Log.Println("error inserting item", item, err)
	} else {
		up.numImportedFiles++
		//log.Log.Println("inserted", item)
	}
	return err
}
<commit_msg>filebase output fixup<commit_after>package db

import (
	"config"
	"errrs"
	"fmt"
	"github.com/coopernurse/gorp"
	log "logger"
	"os"
	osuser "os/user"
	"path"
	"path/filepath"
	"strings"
	"taglib"
)

type entry struct {
	folder string
	file   string
}

const bufferSize = 128

type updater struct {
	tx           chan *gorp.Transaction
	allFiles     chan entry // all files seen
	newFiles     chan entry // files not yet in db
	importFiles  chan entry // files to import
	success      chan bool
	stopStepping chan bool

	// the receiving goroutine shall increment these
	numAllFiles      int
	numNewFiles      int
	numImportFiles   int
	numInvalidFiles  int
	numFailedFiles   int
	numImportedFiles int
}

var IgnoredTypes = []string{
	"jpg", "jpeg", "png", "gif", "nfo", "m3u", "log", "sfv", "txt", "cue",
	"itc2", "html", "xml", "ipa", "asd", "plist", "itdb", "itl", "tmp", "ini",
	"sh", "sha1", "blb"}

func (d *DB) Update() {
	// keep file base up to date
	searchPath := config.Current.MediaPath
	if strings.Contains(searchPath, "~") {
		user, err := osuser.Current()
		if err != nil {
			log.Log.Println("Error getting user home directory:", err)
			return
		}
		searchPath = strings.Replace(searchPath, "~", user.HomeDir, -1)
	}
	if _, err := os.Stat(searchPath); os.IsNotExist(err) {
		log.Log.Println("Error: Music path", searchPath, "does not exist!")
		return
	}
	tx, err := d.dbmap.Begin()
	if err != nil {
		log.Log.Println("Could not start db transaction")
		return
	}
	up := &updater{tx: make(chan *gorp.Transaction, 1),
		allFiles:     make(chan entry, bufferSize),
		newFiles:     make(chan entry, bufferSize),
		importFiles:  make(chan entry, bufferSize),
		success:      make(chan bool),
		stopStepping: make(chan bool, 1)}
	up.tx <- tx

	go func() {
		err := filepath.Walk(searchPath, up.step)
		if err != nil {
			log.Log.Println("Updater error:", err)
		}
		close(up.allFiles)
	}()

	go func(input, output chan entry) {
		for entry := range input {
			up.numAllFiles++
			//fmt.Println("suffix filter gets:", entry)
			do := true
			for _, v := range IgnoredTypes {
				if strings.HasSuffix(entry.file, v) {
					//TODO do something with the cover jpgs
					do = false
					break
				}
			}
			if do {
				output <- entry
			}
		}
		close(output)
	}(up.allFiles, up.importFiles)

	go func(input, output chan entry) {
		for entry := range input {
			up.numImportFiles++
			//fmt.Println("seen filter gets:", entry)
			// check if we already did this one
			itemPath := ItemPathView{}
			tx := <-up.tx
			if err := tx.SelectOne(&itemPath,
				`select item_id Id, filename Filename, path Path
				from `+ItemTable+`
				join `+FolderTable+` on `+FolderTable+`.folder_id = `+ItemTable+`.folder_id
				where filename = ?
				and path = ?`,
				entry.file, entry.folder); err != nil {
				fmt.Println("sql error:", err)
				up.success <- false
			}
			up.tx <- tx
			if itemPath.Id != 0 {
				// this one is already in the db
				//TODO check if the tags have changed anyway
				//log.Log.Println("skipping", path)
			} else {
				output <- entry
			}
		}
		close(output)
	}(up.importFiles, up.newFiles)

	go func(input chan entry, success chan bool) {
		for entry := range input {
			up.numNewFiles++
			err := up.analyze(path.Join(entry.folder, entry.file),
				entry.folder, entry.file)
			if err != nil {
				up.numFailedFiles++
				fmt.Println("import error: ", err)
			}
		}
		up.success <- true
		close(up.success)
	}(up.newFiles, up.success)

	success := true
	for v := range up.success {
		if !v {
			up.stopStepping <- true
			success = false
			break
			if err = tx.Rollback(); err != nil {
				log.Log.Println("rollback error:", err)
			}
		}
	}
	if success {
		tx := <-up.tx
		if err = tx.Commit(); err != nil {
			log.Log.Println("Updater error:", err)
		}
	}
	log.Log.Println("Filebase updated:",
		"\nTotal Files:      ", up.numAllFiles,
		"\nNon-ignored Files:", up.numImportFiles,
		"\nNew Files:        ", up.numNewFiles,
		"\nImported Files:   ", up.numImportedFiles,
		"\nInvalid/Non-media:", up.numInvalidFiles,
		"\nFailed Files:     ", up.numFailedFiles)
}

func (up *updater) step(file string, info os.FileInfo, err error) error {
	if info == nil ||
		info.Name() == "." ||
		info.Name() == ".." {
		return nil
	}
	if info.IsDir() {
		//log.Log.Println("in", file)
	} else if linked, err := filepath.EvalSymlinks(file); err != nil || file != linked {
		if err != nil {
			log.Log.Println("Error walking files:", err.Error())
			return nil
		}
		filepath.Walk(linked, up.step)
	} else {
		select {
		case <-up.stopStepping:
			return errrs.New("aborting")
		case up.allFiles <- entry{path.Dir(file), info.Name()}:
		}
	}
	return nil
}

func (up *updater) analyze(path string, parent string, file string) error {
	//log.Log.Println("doing", path)

	tag, err := taglib.Read(path)
	if err != nil {
		log.Log.Println("error reading file", path, "-", err)
		up.numInvalidFiles++
		return nil
	}

	defer tag.Close()

	title := tag.Title()
	artist := tag.Artist()
	if title == nil || artist == nil {
		return errrs.New("Title and Artist cannot be nil. File " + path)
	}
	item := &Item{
		Title:       *title,
		Artist:      *artist,
		AlbumArtist: nil,
		Album:       tag.Album(),
		Genre:       tag.Genre(),
		TrackNumber: uint32(tag.Track()),
		Folder:      &Folder{Path: parent},
		Filename:    &file,
	}
	//TODO get album, check ID etc

	tx := <-up.tx
	err = tx.Insert(item)
	up.tx <- tx
	if err != nil {
		log.Log.Println("error inserting item", item, err)
	} else {
		up.numImportedFiles++
		//log.Log.Println("inserted", item)
	}
	return err
}
<|endoftext|>


<commit_before>package date

import (
	"encoding/json"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestRange(t *testing.T) {
	assert.True(t, Infinity().IsInfinity())
	assert.True(t, Empty().IsEmpty())

	assert.True(t, Never().Equals(Empty()))
	assert.True(t, Forever().Equals(Infinity()))
	assert.False(t, Forever().Equals(Empty()))
}

func TestRange_Contains(t *testing.T) {
	year2015 := EntireYear(2015)
	dec := EntireMonth(2015, 12)
	novOnward := Range{Start: New(2015, 11, 1)}
	decOnward := Range{Start: New(2015, 12, 1)}

	assert.True(t, year2015.Contains(dec))
	assert.True(t, dec.DoesNotContain(year2015))

	assert.True(t, novOnward.Contains(dec))
	assert.False(t, year2015.Contains(novOnward))

	assert.True(t, novOnward.Contains(novOnward))

	assert.True(t, novOnward.Contains(decOnward))
	assert.False(t, decOnward.Contains(novOnward))
}

func TestRange_Days(t *testing.T) {
	assert.Equal(t, 0, Empty().Days())
	assert.Equal(t, 1, OnlyToday().Days())
	assert.Equal(t, 365, EntireYear(2015).Days())
	assert.Equal(t, 366, EntireYear(2016).Days())
	assert.Equal(t, 29, EntireMonth(2016, 2).Days())
}

func TestRange_Error(t *testing.T) {
	assert.Nil(t, Never().Error())

	// Unbounded ranges are allowed
	end := Range{End: New(2015, 3, 1)}
	assert.Nil(t, end.Error(), "Unbounded start dates should not error")

	start := Range{Start: New(2015, 3, 1)}
	assert.Nil(t, start.Error(), "Unbounded end dates should not error")

	var invalid Range
	invalid.Start = New(2015, 3, 2)
	invalid.End = New(2015, 3, 1)
	assert.NotNil(t, invalid.Error())
}

func TestRange_Intersection(t *testing.T) {
	year2015 := EntireYear(2015)
	nov := EntireMonth(2015, 11)
	dec := EntireMonth(2015, 12)
	novOnward := Range{Start: nov.Start}
	decOnward := Range{Start: dec.Start}
	untilDec := Range{End: nov.End}
	empty := Empty()

	assert.True(t, empty.Intersection(nov).IsZero())
	assert.Equal(t, dec, novOnward.Intersection(dec))

	assert.Equal(t, nov, year2015.Intersection(nov))

	assert.Equal(t, decOnward, decOnward.Intersection(novOnward))
	assert.Equal(t, decOnward, novOnward.Intersection(decOnward))
	assert.Equal(t, nov, novOnward.Intersection(untilDec))
}

func TestRange_Overlaps(t *testing.T) {
	year2015 := EntireYear(2015)
	nov := EntireMonth(2015, 11)
	dec := EntireMonth(2015, 12)
	novOnward := Range{Start: New(2015, 11, 1)}

	assert.False(t, nov.Overlaps(dec))
	assert.True(t, dec.Overlaps(year2015))
	assert.True(t, nov.Overlaps(SingleDay(New(2015, 11, 30))))
	assert.True(t, novOnward.Overlaps(dec))
}

func TestRange_Marshal(t *testing.T) {
	// Empty ranges should render as null
	b, err := json.Marshal(Never())
	assert.Nil(t, err)
	assert.Equal(t, "null", string(b))

	// Infinite ranges should render as null start and end dates
	b, err = json.Marshal(Infinity())
	assert.Nil(t, err)
	assert.Equal(t, `{"start":null,"end":null}`, string(b))
}

func TestRange_String(t *testing.T) {
	assert.Equal(t, "never", Never().String())
	assert.Equal(t, "forever", Forever().String())
	assert.Equal(t, "2016-02-01 to 2016-02-29", EntireMonth(2016, 2).String())
	assert.Equal(t, "until 2016-02-29", Range{End: New(2016, 2, 29)}.String())
	assert.Equal(t, "2016-02-01 onward", Range{Start: New(2016, 2, 1)}.String())
}

func TestRange_Union(t *testing.T) {
	year2015 := EntireYear(2015)
	jan := EntireMonth(2016, 1)
	union := year2015.Union(jan)

	assert.Equal(t, New(2015, 1, 1), union.Start)
	assert.Equal(t, New(2016, 1, 31), union.End)

	feb := EntireMonth(2016, 2)
	union = jan.Union(feb)
	assert.Equal(t, feb.End, union.End)
	assert.Equal(t, jan.Start, union.Start)

	nov := EntireMonth(2015, 11)
	dec := EntireMonth(2015, 12)
	novOnward := Range{Start: nov.Start}
	decOnward := Range{Start: dec.Start}
	untilDec := Range{End: nov.End}

	assert.Equal(t, novOnward, decOnward.Union(novOnward))
	assert.Equal(t, Forever(), untilDec.Union(decOnward))

	assert.Equal(t, Empty(), Empty().Union(Empty()))
	assert.Equal(t, Forever(), Empty().Union(Forever()))
	assert.Equal(t, Forever(), Forever().Union(Empty()))
}

func TestRange_Unmarshal(t *testing.T) {
	// Unmarshaling should overwrite values
	open := EntireMonth(2015, 2)

	raw := `{"start":"2015-03-01","end":null}`
	assert.Nil(t, json.Unmarshal([]byte(raw), &open))
	assert.Equal(t, New(2015, 3, 1), open.Start)
	assert.True(t, open.End.IsZero())

	// TODO nulls should be unmarshaled as empty ranges
	// raw = `null`
	// var zero Range
	// assert.Nil(t, json.Unmarshal([]byte(raw), &zero))
	// assert.True(t, zero.IsEmpty())
}
<commit_msg>Range tests no longer use testify<commit_after>package date

import (
	"encoding/json"
	"testing"
)

var (
	empty     = Empty()
	year2015  = EntireYear(2015)
	nov       = EntireMonth(2015, 11)
	dec       = EntireMonth(2015, 12)
	novOnward = Range{Start: nov.Start}
	decOnward = Range{Start: dec.Start}
	untilDec  = Range{End: nov.End}
	jan       = EntireMonth(2016, 1)
	feb       = EntireMonth(2016, 2)
)

func TestRange(t *testing.T) {
	if !Infinity().IsInfinity() {
		t.Error("Infinity should be infinity")
	}
	if !Empty().IsEmpty() {
		t.Error("Empty should be empty")
	}
	if !Never().Equals(Empty()) {
		t.Error("Never should equal Empty")
	}
	if !Forever().Equals(Infinity()) {
		t.Error("Forever should equal Infinity")
	}

	if Forever().Equals(Empty()) {
		t.Error("Forever should not equal Empty")
	}
}

func TestRange_Contains(t *testing.T) {
	if !year2015.Contains(dec) {
		t.Error("year 2015 should contain December 2015")
	}
	if !dec.DoesNotContain(year2015) {
		t.Error("December 2015 should not contain the year 2015")
	}

	if !novOnward.Contains(dec) {
		t.Error("November 2015 onward should contain December 2015")
	}
	if year2015.Contains(novOnward) {
		t.Error("Year 2015 should not contain November 2015 onward")
	}

	if !novOnward.Contains(novOnward) {
		t.Error("November 2015 onward should contain itself")
	}

	if !novOnward.Contains(decOnward) {
		t.Error("November 2015 onward should contain December 2015 onward")
	}
	if decOnward.Contains(novOnward) {
		t.Error("December 2015 onward should not contain November 2015 onward")
	}
}

var daysTests = []struct {
	want, have int
}{
	{0, Empty().Days()},
	{1, OnlyToday().Days()},
	{365, EntireYear(2015).Days()},
	{366, EntireYear(2016).Days()},
	{29, EntireMonth(2016, 2).Days()},
}

func TestRange_Days(t *testing.T) {
	for _, test := range daysTests {
		if test.want != test.have {
			t.Errorf("Range Days() want=%d have=%d", test.want, test.have)
		}
	}
}

func TestRange_Error(t *testing.T) {
	if Never().Error() != nil {
		t.Error("Never should not error")
	}

	// Unbounded ranges are allowed
	end := Range{End: New(2015, 3, 1)}
	if end.Error() != nil {
		t.Error("Unbounded start dates should not error")
	}

	start := Range{Start: New(2015, 3, 1)}
	if start.Error() != nil {
		t.Error("Unbounded end dates should not error")
	}

	var invalid Range
	invalid.Start = New(2015, 3, 2)
	invalid.End = New(2015, 3, 1)
	if invalid.Error() == nil {
		t.Error("Invalid ranges should error")
	}
}

var intersectionTests = []struct {
	want, have Range
}{
	{dec, novOnward.Intersection(dec)},
	{nov, year2015.Intersection(nov)},
	{decOnward, decOnward.Intersection(novOnward)},
	{decOnward, novOnward.Intersection(decOnward)},
	{nov, novOnward.Intersection(untilDec)},
}

func TestRange_Intersection(t *testing.T) {
	if !empty.Intersection(nov).IsZero() {
		t.Error("An empty range should have a zero intersection")
	}

	for _, test := range intersectionTests {
		if test.want != test.have {
			t.Errorf(
				"Range Intersection() want=%v have=%v", test.want, test.have,
			)
		}
	}
}

func TestRange_Overlaps(t *testing.T) {
	if nov.Overlaps(dec) {
		t.Error("November should not overlap December")
	}
	if !dec.Overlaps(year2015) {
		t.Error("December 2015 should overlap the year 2015")
	}
	if !nov.Overlaps(SingleDay(New(2015, 11, 30))) {
		t.Error("November 2015 should overlap 2015-11-30")
	}
	if !novOnward.Overlaps(dec) {
		t.Error("November 2015 onward should overlap December 2015")
	}
}

func TestRange_Marshal(t *testing.T) {
	// Empty ranges should render as null
	b, err := json.Marshal(Never())
	if err != nil {
		t.Fatal("json.Marshal of Never() should not error")
	}
	if string(b) != "null" {
		t.Error(`json.Marshal of Never() should be null`)
	}

	// Infinite ranges should render as null start and end dates
	b, err = json.Marshal(Infinity())
	if err != nil {
		t.Fatal("json.Marshal of Infinity() should not error")
	}
	if string(b) != `{"start":null,"end":null}` {
		t.Error(
			`json.Marshal of Infinity() should be {"start":null,"end":null}`,
		)
	}
}

var stringTests = []struct {
	want, have string
}{
	{"never", Never().String()},
	{"forever", Forever().String()},
	{"2016-02-01 to 2016-02-29", EntireMonth(2016, 2).String()},
	{"until 2016-02-29", Range{End: New(2016, 2, 29)}.String()},
	{"2016-02-01 onward", Range{Start: New(2016, 2, 1)}.String()},
}

func TestRange_String(t *testing.T) {
	for _, test := range stringTests {
		if test.want != test.have {
			t.Errorf("Range String() want=%s have=%s", test.want, test.have)
		}
	}
}

func TestRange_Union(t *testing.T) {
	union := year2015.Union(jan)
	if New(2015, 1, 1) != union.Start {
		t.Error(
			"The union of 2015 and January 2016 should start on 2015-01-01",
		)
	}
	if New(2016, 1, 31) != union.End {
		t.Error(
			"The union of 2015 and January 2016 should end on 2016-01-31",
		)
	}

	union = jan.Union(feb)
	if jan.Start != union.Start {
		t.Error(
			"The union of January and February 2016 should start on 2016-01-01",
		)
	}
	if feb.End != union.End {
		t.Error(
			"The union of January and February 2016 should end on 2016-02-29",
		)
	}

	if decOnward.Union(novOnward) != novOnward {
		t.Error("The union of November onward and December onward should be Novermber onward")
	}
	if untilDec.Union(decOnward) != Forever() {
		t.Error("The union of until December and December onward should be forever")
	}

	if Empty().Union(Empty()) != Empty() {
		t.Error("The union of two empty ranges should be empty")
	}
	if Empty().Union(Forever()) != Forever() {
		t.Error("The union of a forever range should be forever")
	}
	if Forever().Union(Empty()) != Forever() {
		t.Error("The union of a forever range should be forever")
	}
}

func TestRange_Unmarshal(t *testing.T) {
	// Unmarshaling should overwrite values
	open := EntireMonth(2015, 2)

	raw := `{"start":"2015-03-01","end":null}`
	if json.Unmarshal([]byte(raw), &open) != nil {
		t.Error("json.Unmarshal should not error")
	}

	if New(2015, 3, 1) != open.Start {
		t.Error("The start date after json.Unmarshal should be 2015-03-01")
	}
	if !open.End.IsZero() {
		t.Error("The end date after json.Unmarshal should be zero")
	}

	// TODO nulls should be unmarshaled as empty ranges
	// raw = `null`
	// var zero Range
	// if json.Unmarshal([]byte(raw), &zero) != nil {
	// 	t.Error("json.Unmarshal should not error for null ranges")
	// }
	// if !zero.IsEmpty() {
	// 	t.Error("null ranges after json.Unmarshal should be empty")
	// }
}
<|endoftext|>