<commit_before>/*
 * Copyright (C) 2017 Sam Kumar, Michael Andersen, and the University
 * of California, Berkeley.
 *
 * This file is part of Mr. Plotter (the Multi-Resolution Plotter).
 *
 * Mr. Plotter is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Mr. Plotter is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Mr. Plotter.  If not, see <http://www.gnu.org/licenses/>.
 */

/* Handles metadata requests. */

package main

import (
	"context"
	"errors"
	"sort"
	"strings"

	"gopkg.in/btrdb.v4"

	"github.com/SoftwareDefinedBuildings/mr-plotter/accounts"
	etcd "github.com/coreos/etcd/clientv3"
	"github.com/pborman/uuid"
)

var defaulttagset = map[string]struct{}{accounts.PublicTag: struct{}{}}

var btrdbSeparator byte = '/'

const plotterSeparator = '/'

func streamtoleafname(ctx context.Context, s *btrdb.Stream) (string, error) {
	var name string
	var ok bool

	ann, _, err := s.CachedAnnotations(ctx)
	if err != nil {
		return "", err
	}

	name, ok = ann["name"]
	if ok {
		return name, nil
	}

	tags, err := s.Tags(ctx)
	if err != nil {
		return "", err
	}

	name, ok = tags["name"]
	if ok {
		return name, nil
	}

	return "$" + s.UUID().String(), nil
}

func leafnametostream(ctx context.Context, bc *btrdb.BTrDB, collection string, leafname string) (*btrdb.Stream, error) {
	if len(leafname) != 0 && leafname[0] == '$' {
		uuidstr := leafname[1:]
		uu := uuid.Parse(uuidstr)
		s := bc.StreamFromUUID(uu)
		ex, err := s.Exists(ctx)
		if err != nil {
			return nil, err
		}
		if !ex {
			return nil, nil
		}
		return s, nil
	}
	matching, err := bc.LookupStreams(ctx, collection, false, map[string]*string{"name": &leafname}, nil)
	if err != nil {
		return nil, err
	}
	if len(matching) == 0 {
		return nil, nil
	}
	return matching[0], nil
}

func getprefixes(ctx context.Context, ec *etcd.Client, ls *LoginSession) (map[string]struct{}, error) {
	var tagset map[string]struct{}
	if ls == nil {
		tagset = defaulttagset
	} else {
		tagset = ls.Tags
	}

	prefixes := make(map[string]struct{})
	for tagname := range tagset {
		tagdef, err := accounts.RetrieveTagDef(ctx, ec, tagname)
		if err != nil {
			return nil, err
		}

		// Tags that are not defined do not grant any permissions
		if tagdef != nil {
			for pfx := range tagdef.PathPrefix {
				prefixes[pfx] = struct{}{}
			}
		}
	}

	return prefixes, nil
}

/* Returns a sorted slice of top level elements in the stream tree. */
func treetopPaths(ctx context.Context, ec *etcd.Client, bc *btrdb.BTrDB, ls *LoginSession) ([]string, error) {
	collections, err := bc.ListAllCollections(ctx)
	if err != nil {
		return nil, err
	}

	prefixes, err := getprefixes(ctx, ec, ls)
	if err != nil {
		return nil, err
	}

	toplevelset := make(map[string]struct{})
	for _, coll := range collections {
		var toplevel string

		/* Skip this collection if the user doesn't have permission. */
		haspermission := false
		for pfx := range prefixes {
			if strings.HasPrefix(coll, pfx) {
				haspermission = true
				break
			}
		}
		if !haspermission {
			continue
		}

		/* Extract the top-level element from the collection name. */
		sepindex := strings.Index(coll, string(btrdbSeparator))
		/* If the element starts with the separator, then we would get an empty
		 * toplevel element. To avoid this, split on the next separator. */
		if sepindex == 0 {
			sepindex = strings.Index(coll[1:], string(btrdbSeparator))
			if sepindex != -1 {
				sepindex++
			}
		}
		if sepindex == -1 {
			toplevel = coll
		} else {
			toplevel = coll[:sepindex]
		}
		toplevelset[toplevel] = struct{}{}
	}

	/* Transfer top-level elements into a slice. */
	treetop := make([]string, len(toplevelset))
	i := 0
	for toplevel := range toplevelset {
		treetop[i] = toplevel
		i++
	}

	sort.Strings(treetop)

	return treetop, nil
}

func treebranchPaths(ctx context.Context, ec *etcd.Client, bc *btrdb.BTrDB, ls *LoginSession, toplevel string) ([]string, error) {
	collprefix := toplevel + string(btrdbSeparator)
	collections, err := bc.ListCollections(ctx, collprefix)
	if err != nil {
		return nil, err
	}

	prefixes, err := getprefixes(ctx, ec, ls)
	if err != nil {
		return nil, err
	}

	branches := make([]string, 0, len(collections))
	for _, coll := range collections {
		/* Skip this collection if the user doesn't have permission. */
		haspermission := false
		for pfx := range prefixes {
			if strings.HasPrefix(coll, pfx) {
				haspermission = true
				break
			}
		}
		if !haspermission {
			continue
		}

		dotidx := strings.IndexByte(coll, btrdbSeparator)
		if dotidx == -1 {
			dotidx = len(coll)
		}
		pathcoll := strings.Replace(coll[dotidx:], string(btrdbSeparator), string(plotterSeparator), -1)

		branches = append(branches, pathcoll)
	}

	sort.Strings(branches)

	return branches, nil
}

func treeleafPaths(ctx context.Context, ec *etcd.Client, bc *btrdb.BTrDB, ls *LoginSession, branchpath string) ([]string, error) {
	coll := strings.Replace(branchpath, string(plotterSeparator), string(btrdbSeparator), -1)

	/* Get the streams in the collection. */
	streams, err := bc.LookupStreams(ctx, coll, false, nil, nil)
	if err != nil {
		return nil, err
	}

	leaves := make([]string, 0, len(streams))
	for _, stream := range streams {
		/* Formulate the path for this stream. */
		pathfin, err := streamtoleafname(ctx, stream)
		if err != nil {
			return nil, err
		}
		path := string(plotterSeparator) + pathfin

		/* Add path to return slice. */
		leaves = append(leaves, path)
	}

	sort.Strings(leaves)
	return leaves, nil
}

func treeleafMetadata(ctx context.Context, ec *etcd.Client, bc *btrdb.BTrDB, ls *LoginSession, path string) (map[string]interface{}, error) {
	div := strings.LastIndex(path, string(plotterSeparator))
	if div == -1 {
		return nil, errors.New("Invalid path")
	}
	leafname := path[div+1:]
	collection := strings.Replace(path[:div], string(plotterSeparator), string(btrdbSeparator), -1)
	s, err := leafnametostream(ctx, bc, collection, leafname)
	if err != nil {
		return nil, err
	}
	if s == nil {
		return nil, errors.New("Stream does not exist")
	}

	uu := s.UUID()
	return uuidMetadata(ctx, ec, bc, ls, uu)
}

func uuidMetadata(ctx context.Context, ec *etcd.Client, bc *btrdb.BTrDB, ls *LoginSession, uu uuid.UUID) (map[string]interface{}, error) {
	s := bc.StreamFromUUID(uu)
	ex, err := s.Exists(ctx)
	if err != nil {
		return nil, err
	}
	if !ex {
		return nil, errors.New("Stream does not exist")
	}
	if !hasPermission(ctx, ls, uu) {
		return nil, errors.New("Need permission")
	}

	ann, _, err := s.CachedAnnotations(ctx)
	if err != nil {
		return nil, err
	}

	tags, err := s.Tags(ctx)
	if err != nil {
		return nil, err
	}

	collection, err := s.Collection(ctx)
	if err != nil {
		return nil, err
	}
	pathfin, err := streamtoleafname(ctx, s)
	if err != nil {
		return nil, err
	}

	var doc = map[string]interface{}{
		"annotations": ann,
		"tags":        tags,
		"path":        strings.Replace(collection, string(btrdbSeparator), string(plotterSeparator), -1) + string(plotterSeparator) + pathfin,
		"uuid":        uu.String(),
	}

	return doc, nil
}
<commit_msg>Fix bug in tree request resolution<commit_after>/*
 * Copyright (C) 2017 Sam Kumar, Michael Andersen, and the University
 * of California, Berkeley.
 *
 * This file is part of Mr. Plotter (the Multi-Resolution Plotter).
 *
 * Mr. Plotter is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Mr. Plotter is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Mr. Plotter.  If not, see <http://www.gnu.org/licenses/>.
 */

/* Handles metadata requests. */

package main

import (
	"context"
	"errors"
	"sort"
	"strings"

	"gopkg.in/btrdb.v4"

	"github.com/SoftwareDefinedBuildings/mr-plotter/accounts"
	etcd "github.com/coreos/etcd/clientv3"
	"github.com/pborman/uuid"
)

var defaulttagset = map[string]struct{}{accounts.PublicTag: struct{}{}}

var btrdbSeparator byte = '/'

const plotterSeparator = '/'

func streamtoleafname(ctx context.Context, s *btrdb.Stream) (string, error) {
	var name string
	var ok bool

	ann, _, err := s.CachedAnnotations(ctx)
	if err != nil {
		return "", err
	}

	name, ok = ann["name"]
	if ok {
		return name, nil
	}

	tags, err := s.Tags(ctx)
	if err != nil {
		return "", err
	}

	name, ok = tags["name"]
	if ok {
		return name, nil
	}

	return "$" + s.UUID().String(), nil
}

func leafnametostream(ctx context.Context, bc *btrdb.BTrDB, collection string, leafname string) (*btrdb.Stream, error) {
	if len(leafname) != 0 && leafname[0] == '$' {
		uuidstr := leafname[1:]
		uu := uuid.Parse(uuidstr)
		s := bc.StreamFromUUID(uu)
		ex, err := s.Exists(ctx)
		if err != nil {
			return nil, err
		}
		if !ex {
			return nil, nil
		}
		return s, nil
	}
	matching, err := bc.LookupStreams(ctx, collection, false, nil, map[string]*string{"name": &leafname})
	if err != nil {
		return nil, err
	}
	if len(matching) == 0 {
		matching, err = bc.LookupStreams(ctx, collection, false, map[string]*string{"name": &leafname}, nil)
		if err != nil {
			return nil, err
		}
		if len(matching) == 0 {
			return nil, nil
		}
	}
	return matching[0], nil
}

func getprefixes(ctx context.Context, ec *etcd.Client, ls *LoginSession) (map[string]struct{}, error) {
	var tagset map[string]struct{}
	if ls == nil {
		tagset = defaulttagset
	} else {
		tagset = ls.Tags
	}

	prefixes := make(map[string]struct{})
	for tagname := range tagset {
		tagdef, err := accounts.RetrieveTagDef(ctx, ec, tagname)
		if err != nil {
			return nil, err
		}

		// Tags that are not defined do not grant any permissions
		if tagdef != nil {
			for pfx := range tagdef.PathPrefix {
				prefixes[pfx] = struct{}{}
			}
		}
	}

	return prefixes, nil
}

/* Returns a sorted slice of top level elements in the stream tree. */
func treetopPaths(ctx context.Context, ec *etcd.Client, bc *btrdb.BTrDB, ls *LoginSession) ([]string, error) {
	collections, err := bc.ListAllCollections(ctx)
	if err != nil {
		return nil, err
	}

	prefixes, err := getprefixes(ctx, ec, ls)
	if err != nil {
		return nil, err
	}

	toplevelset := make(map[string]struct{})
	for _, coll := range collections {
		var toplevel string

		/* Skip this collection if the user doesn't have permission. */
		haspermission := false
		for pfx := range prefixes {
			if strings.HasPrefix(coll, pfx) {
				haspermission = true
				break
			}
		}
		if !haspermission {
			continue
		}

		/* Extract the top-level element from the collection name. */
		sepindex := strings.Index(coll, string(btrdbSeparator))
		/* If the element starts with the separator, then we would get an empty
		 * toplevel element. To avoid this, split on the next separator. */
		if sepindex == 0 {
			sepindex = strings.Index(coll[1:], string(btrdbSeparator))
			if sepindex != -1 {
				sepindex++
			}
		}
		if sepindex == -1 {
			toplevel = coll
		} else {
			toplevel = coll[:sepindex]
		}
		toplevelset[toplevel] = struct{}{}
	}

	/* Transfer top-level elements into a slice. */
	treetop := make([]string, len(toplevelset))
	i := 0
	for toplevel := range toplevelset {
		treetop[i] = toplevel
		i++
	}

	sort.Strings(treetop)

	return treetop, nil
}

func treebranchPaths(ctx context.Context, ec *etcd.Client, bc *btrdb.BTrDB, ls *LoginSession, toplevel string) ([]string, error) {
	collprefix := toplevel + string(btrdbSeparator)
	collections, err := bc.ListCollections(ctx, collprefix)
	if err != nil {
		return nil, err
	}

	prefixes, err := getprefixes(ctx, ec, ls)
	if err != nil {
		return nil, err
	}

	branches := make([]string, 0, len(collections))
	for _, coll := range collections {
		/* Skip this collection if the user doesn't have permission. */
		haspermission := false
		for pfx := range prefixes {
			if strings.HasPrefix(coll, pfx) {
				haspermission = true
				break
			}
		}
		if !haspermission {
			continue
		}

		dotidx := strings.IndexByte(coll, btrdbSeparator)
		if dotidx == -1 {
			dotidx = len(coll)
		}
		pathcoll := strings.Replace(coll[dotidx:], string(btrdbSeparator), string(plotterSeparator), -1)

		branches = append(branches, pathcoll)
	}

	sort.Strings(branches)

	return branches, nil
}

func treeleafPaths(ctx context.Context, ec *etcd.Client, bc *btrdb.BTrDB, ls *LoginSession, branchpath string) ([]string, error) {
	coll := strings.Replace(branchpath, string(plotterSeparator), string(btrdbSeparator), -1)

	/* Get the streams in the collection. */
	streams, err := bc.LookupStreams(ctx, coll, false, nil, nil)
	if err != nil {
		return nil, err
	}

	leaves := make([]string, 0, len(streams))
	for _, stream := range streams {
		/* Formulate the path for this stream. */
		pathfin, err := streamtoleafname(ctx, stream)
		if err != nil {
			return nil, err
		}
		path := string(plotterSeparator) + pathfin

		/* Add path to return slice. */
		leaves = append(leaves, path)
	}

	sort.Strings(leaves)
	return leaves, nil
}

func treeleafMetadata(ctx context.Context, ec *etcd.Client, bc *btrdb.BTrDB, ls *LoginSession, path string) (map[string]interface{}, error) {
	div := strings.LastIndex(path, string(plotterSeparator))
	if div == -1 {
		return nil, errors.New("Invalid path")
	}
	leafname := path[div+1:]
	collection := strings.Replace(path[:div], string(plotterSeparator), string(btrdbSeparator), -1)
	s, err := leafnametostream(ctx, bc, collection, leafname)
	if err != nil {
		return nil, err
	}
	if s == nil {
		return nil, errors.New("Stream does not exist")
	}

	uu := s.UUID()
	return uuidMetadata(ctx, ec, bc, ls, uu)
}

func uuidMetadata(ctx context.Context, ec *etcd.Client, bc *btrdb.BTrDB, ls *LoginSession, uu uuid.UUID) (map[string]interface{}, error) {
	s := bc.StreamFromUUID(uu)
	ex, err := s.Exists(ctx)
	if err != nil {
		return nil, err
	}
	if !ex {
		return nil, errors.New("Stream does not exist")
	}
	if !hasPermission(ctx, ls, uu) {
		return nil, errors.New("Need permission")
	}

	ann, _, err := s.CachedAnnotations(ctx)
	if err != nil {
		return nil, err
	}

	tags, err := s.Tags(ctx)
	if err != nil {
		return nil, err
	}

	collection, err := s.Collection(ctx)
	if err != nil {
		return nil, err
	}
	pathfin, err := streamtoleafname(ctx, s)
	if err != nil {
		return nil, err
	}

	var doc = map[string]interface{}{
		"annotations": ann,
		"tags":        tags,
		"path":        strings.Replace(collection, string(btrdbSeparator), string(plotterSeparator), -1) + string(plotterSeparator) + pathfin,
		"uuid":        uu.String(),
	}

	return doc, nil
}
<|endoftext|>


<commit_before>package terraform

import (
	"fmt"

	"github.com/hashicorp/go-version"
)

// The main version number that is being run at the moment.
const Version = "0.9.2"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
const VersionPrerelease = "dev"

// SemVersion is an instance of version.Version. This has the secondary
// benefit of verifying during tests and init time that our version is a
// proper semantic version, which should always be the case.
var SemVersion = version.Must(version.NewVersion(Version))

// VersionHeader is the header name used to send the current terraform version
// in http requests.
const VersionHeader = "Terraform-Version"

func VersionString() string {
	if VersionPrerelease != "" {
		return fmt.Sprintf("%s-%s", Version, VersionPrerelease)
	}
	return Version
}
<commit_msg>v0.9.2<commit_after>package terraform

import (
	"fmt"

	"github.com/hashicorp/go-version"
)

// The main version number that is being run at the moment.
const Version = "0.9.2"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
const VersionPrerelease = ""

// SemVersion is an instance of version.Version. This has the secondary
// benefit of verifying during tests and init time that our version is a
// proper semantic version, which should always be the case.
var SemVersion = version.Must(version.NewVersion(Version))

// VersionHeader is the header name used to send the current terraform version
// in http requests.
const VersionHeader = "Terraform-Version"

func VersionString() string {
	if VersionPrerelease != "" {
		return fmt.Sprintf("%s-%s", Version, VersionPrerelease)
	}
	return Version
}
<|endoftext|>


<commit_before>package terraform

import (
	"fmt"

	"github.com/hashicorp/go-version"
)

// The main version number that is being run at the moment.
const Version = "0.10.0-rc1"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
var VersionPrerelease = "dev"

// SemVersion is an instance of version.Version. This has the secondary
// benefit of verifying during tests and init time that our version is a
// proper semantic version, which should always be the case.
var SemVersion = version.Must(version.NewVersion(Version))

// VersionHeader is the header name used to send the current terraform version
// in http requests.
const VersionHeader = "Terraform-Version"

func VersionString() string {
	if VersionPrerelease != "" {
		return fmt.Sprintf("%s-%s", Version, VersionPrerelease)
	}
	return Version
}
<commit_msg>release: clean up after v0.10.0-rc1<commit_after>package terraform

import (
	"fmt"

	"github.com/hashicorp/go-version"
)

// The main version number that is being run at the moment.
const Version = "0.10.0"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
var VersionPrerelease = "dev"

// SemVersion is an instance of version.Version. This has the secondary
// benefit of verifying during tests and init time that our version is a
// proper semantic version, which should always be the case.
var SemVersion = version.Must(version.NewVersion(Version))

// VersionHeader is the header name used to send the current terraform version
// in http requests.
const VersionHeader = "Terraform-Version"

func VersionString() string {
	if VersionPrerelease != "" {
		return fmt.Sprintf("%s-%s", Version, VersionPrerelease)
	}
	return Version
}
<|endoftext|>


<commit_before>package providerrefresh

import (
	"context"
	"strings"
	"sync"
	"time"

	v32 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"

	"github.com/rancher/rancher/pkg/auth/providers"
	"github.com/rancher/rancher/pkg/auth/settings"
	"github.com/rancher/rancher/pkg/auth/tokens"
	v3 "github.com/rancher/rancher/pkg/generated/norman/management.cattle.io/v3"
	"github.com/rancher/rancher/pkg/types/config"
	"github.com/sirupsen/logrus"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
)

var (
	specialFalse = false
	falsePointer = &specialFalse
)

type UserAuthRefresher interface {
	TriggerAllUserRefresh()
	TriggerUserRefresh(string, bool)
}

func NewUserAuthRefresher(ctx context.Context, scaledContext *config.ScaledContext) UserAuthRefresher {
	return &refresher{
		tokenLister:         scaledContext.Management.Tokens("").Controller().Lister(),
		tokens:              scaledContext.Management.Tokens(""),
		userLister:          scaledContext.Management.Users("").Controller().Lister(),
		tokenMGR:            tokens.NewManager(ctx, scaledContext),
		userAttributes:      scaledContext.Management.UserAttributes(""),
		userAttributeLister: scaledContext.Management.UserAttributes("").Controller().Lister(),
	}
}

type refresher struct {
	sync.Mutex
	tokenLister         v3.TokenLister
	tokens              v3.TokenInterface
	tokenMGR            *tokens.Manager
	userLister          v3.UserLister
	userAttributes      v3.UserAttributeInterface
	userAttributeLister v3.UserAttributeLister
	intervalInSeconds   int64
	unparsedMaxAge      string
	maxAge              time.Duration
}

func (r *refresher) ensureMaxAgeUpToDate(maxAge string) {
	if r.unparsedMaxAge == maxAge || maxAge == "" {
		return
	}

	parsed, err := ParseMaxAge(maxAge)
	if err != nil {
		logrus.Errorf("Error parsing max age %v", err)
		return
	}
	r.unparsedMaxAge = maxAge
	r.maxAge = parsed
}

func (r *refresher) TriggerUserRefresh(userName string, force bool) {
	if force {
		logrus.Debugf("Triggering auth refresh manually on %v", userName)
	} else {
		logrus.Debugf("Triggering auth refresh on %v", userName)
	}
	r.Lock()
	r.ensureMaxAgeUpToDate(settings.AuthUserInfoMaxAgeSeconds.Get())
	r.Unlock()
	if !force && (r.maxAge <= 0) {
		logrus.Debugf("Skipping refresh trigger on user %v because max age setting is <= 0", userName)
		return
	}

	r.triggerUserRefresh(userName, force)
}

func (r *refresher) TriggerAllUserRefresh() {
	logrus.Debug("Triggering auth refresh manually on all users")
	r.refreshAll(true)
}

func (r *refresher) refreshAll(force bool) {
	users, err := r.userLister.List("", labels.Everything())
	if err != nil {
		logrus.Errorf("Error listing Users during auth provider refresh: %v", err)
	}
	for _, user := range users {
		r.triggerUserRefresh(user.Name, force)
	}
}

func (r *refresher) triggerUserRefresh(userName string, force bool) {
	attribs, needCreate, err := r.tokenMGR.EnsureAndGetUserAttribute(userName)
	if err != nil {
		logrus.Errorf("Error fetching user attribute to trigger refresh: %v", err)
		return
	}
	now := time.Now().UTC()
	// in the case there is an invalid (or no) last refresh ignore the error, lastrefresh will be 0
	lastRefresh, _ := time.Parse(time.RFC3339, attribs.LastRefresh)
	earliestRefresh := lastRefresh.Add(r.maxAge)
	if !force && now.Before(earliestRefresh) {
		logrus.Debugf("Skipping refresh for %v due to max-age", userName)
		return
	}

	user, err := r.userLister.Get("", userName)
	if err != nil {
		logrus.Errorf("Error finding user before triggering refresh %v", err)
		return
	}

	for _, principalID := range user.PrincipalIDs {
		if strings.HasPrefix(principalID, "system://") {
			logrus.Debugf("Skipping refresh for system-user %v ", userName)
			return
		}
	}

	attribs.NeedsRefresh = true
	if needCreate {
		_, err := r.userAttributes.Create(attribs)
		if err != nil {
			logrus.Errorf("Error creating user attribute to trigger refresh: %v", err)
		}
	} else {
		_, err = r.userAttributes.Update(attribs)
		if err != nil {
			if apierrors.IsConflict(err) {
				// User attribute has just been updated, triggering the refresh.
				logrus.Debugf("Error updating user attribute to trigger refresh: %v", err)
			} else {
				logrus.Errorf("Error updating user attribute to trigger refresh: %v", err)
			}
		}
	}
}

func (r *refresher) refreshAttributes(attribs *v3.UserAttribute) (*v3.UserAttribute, error) {
	var (
		derivedTokens         []*v3.Token
		loginTokenList        []*v3.Token
		loginTokens           map[string][]*v3.Token
		canLogInAtAll         bool
		errorConfirmingLogins bool
	)

	attribs = attribs.DeepCopy()

	user, err := r.userLister.Get("", attribs.Name)
	if err != nil {
		return nil, err
	}

	loginTokens = make(map[string][]*v3.Token)

	allTokens, err := r.tokenLister.List("", labels.Everything())
	if err != nil {
		return nil, err
	}

	for providerName := range providers.ProviderNames {
		loginTokens[providerName] = []*v3.Token{}
	}

	for _, token := range allTokens {
		if token.UserID != user.Name {
			continue
		}

		if token.IsDerived {
			derivedTokens = append(derivedTokens, token)
		} else {
			loginTokens[token.AuthProvider] = append(loginTokens[token.AuthProvider], token)
			loginTokenList = append(loginTokenList, token)
		}
	}

	for providerName := range providers.ProviderNames {
		// We have to find out if the user has a userprincipal for the provider.
		prefix := providerName + "_user://"
		if providerName == "local" {
			prefix = "local://"
		}
		principalID := ""
		for _, id := range user.PrincipalIDs {
			if strings.HasPrefix(id, prefix) {
				principalID = id
				break
			}
		}
		var newGroupPrincipals []v3.Principal

		// If there is no principalID for the provider, there is no reason to go through the refetch process
		if principalID != "" {
			secret := ""
			if providers.ProvidersWithSecrets[providerName] {
				secret, err = r.tokenMGR.GetSecret(user.Name, providerName, loginTokens[providerName])
				if err != nil {
					return nil, err
				}
			}

			// SAML cannot refresh, so we do restore the existing providers.
			if providers.UnrefreshableProviders[providerName] {
				existingPrincipals := attribs.GroupPrincipals[providerName].Items
				if existingPrincipals != nil {
					newGroupPrincipals = existingPrincipals
				}
			} else {
				newGroupPrincipals, err = providers.RefetchGroupPrincipals(principalID, providerName, secret)
				if err != nil {
					// In the case that we cant access a server, we still want to continue refreshing, but
					// we no longer want to disable derived tokens, or remove their login tokens for this provider
					if err.Error() != "no access" {
						errorConfirmingLogins = true
						logrus.Errorf("Error refreshing token principals, skipping: %v", err)
						existingPrincipals := attribs.GroupPrincipals[providerName].Items
						if existingPrincipals != nil {
							newGroupPrincipals = existingPrincipals
						}
						continue
					}

					// In the case that the user explicitly cannot login at all to this provider
					// (e.g. they no longer exist) we pretend they have no principal with this provider
					// so that their login tokens get blanked out
					principalID = ""

				}
			}
		}

		if len(newGroupPrincipals) == 0 {
			newGroupPrincipals = nil
		}

		attribs.GroupPrincipals[providerName] = v32.Principals{Items: newGroupPrincipals}

		canAccessProvider := false

		if principalID != "" {
			// We want to verify that the user still has rancher access
			canStillAccess, err := providers.CanAccessWithGroupProviders(providerName, principalID, newGroupPrincipals)
			if err != nil {
				return nil, err
			}

			if canStillAccess {
				canAccessProvider = true
				canLogInAtAll = true
			}
		}

		// If the user doesn't have access through this provider, we want to remove their login tokens for this provider
		if !canAccessProvider {
			for _, token := range loginTokens[providerName] {
				err := r.tokens.Delete(token.Name, &metav1.DeleteOptions{})
				if err != nil {
					if apierrors.IsNotFound(err) {
						continue
					}
					return nil, err
				}
			}
		}
	}

	// if they can still log in, or we failed to validate one of their logins, don't disable derived tokens
	if canLogInAtAll || errorConfirmingLogins {
		return attribs, nil
	}

	for _, token := range derivedTokens {
		token.Enabled = falsePointer
		_, err := r.tokenMGR.UpdateToken(token)
		if err != nil {
			return nil, err
		}
	}

	return attribs, nil
}
<commit_msg>Ignore missing secrets when user's token secret is not found<commit_after>package providerrefresh

import (
	"context"
	"strings"
	"sync"
	"time"

	v32 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"

	"github.com/rancher/rancher/pkg/auth/providers"
	"github.com/rancher/rancher/pkg/auth/settings"
	"github.com/rancher/rancher/pkg/auth/tokens"
	v3 "github.com/rancher/rancher/pkg/generated/norman/management.cattle.io/v3"
	"github.com/rancher/rancher/pkg/types/config"
	"github.com/sirupsen/logrus"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
)

var (
	specialFalse = false
	falsePointer = &specialFalse
)

type UserAuthRefresher interface {
	TriggerAllUserRefresh()
	TriggerUserRefresh(string, bool)
}

func NewUserAuthRefresher(ctx context.Context, scaledContext *config.ScaledContext) UserAuthRefresher {
	return &refresher{
		tokenLister:         scaledContext.Management.Tokens("").Controller().Lister(),
		tokens:              scaledContext.Management.Tokens(""),
		userLister:          scaledContext.Management.Users("").Controller().Lister(),
		tokenMGR:            tokens.NewManager(ctx, scaledContext),
		userAttributes:      scaledContext.Management.UserAttributes(""),
		userAttributeLister: scaledContext.Management.UserAttributes("").Controller().Lister(),
	}
}

type refresher struct {
	sync.Mutex
	tokenLister         v3.TokenLister
	tokens              v3.TokenInterface
	tokenMGR            *tokens.Manager
	userLister          v3.UserLister
	userAttributes      v3.UserAttributeInterface
	userAttributeLister v3.UserAttributeLister
	intervalInSeconds   int64
	unparsedMaxAge      string
	maxAge              time.Duration
}

func (r *refresher) ensureMaxAgeUpToDate(maxAge string) {
	if r.unparsedMaxAge == maxAge || maxAge == "" {
		return
	}

	parsed, err := ParseMaxAge(maxAge)
	if err != nil {
		logrus.Errorf("Error parsing max age %v", err)
		return
	}
	r.unparsedMaxAge = maxAge
	r.maxAge = parsed
}

func (r *refresher) TriggerUserRefresh(userName string, force bool) {
	if force {
		logrus.Debugf("Triggering auth refresh manually on %v", userName)
	} else {
		logrus.Debugf("Triggering auth refresh on %v", userName)
	}
	r.Lock()
	r.ensureMaxAgeUpToDate(settings.AuthUserInfoMaxAgeSeconds.Get())
	r.Unlock()
	if !force && (r.maxAge <= 0) {
		logrus.Debugf("Skipping refresh trigger on user %v because max age setting is <= 0", userName)
		return
	}

	r.triggerUserRefresh(userName, force)
}

func (r *refresher) TriggerAllUserRefresh() {
	logrus.Debug("Triggering auth refresh manually on all users")
	r.refreshAll(true)
}

func (r *refresher) refreshAll(force bool) {
	users, err := r.userLister.List("", labels.Everything())
	if err != nil {
		logrus.Errorf("Error listing Users during auth provider refresh: %v", err)
	}
	for _, user := range users {
		r.triggerUserRefresh(user.Name, force)
	}
}

func (r *refresher) triggerUserRefresh(userName string, force bool) {
	attribs, needCreate, err := r.tokenMGR.EnsureAndGetUserAttribute(userName)
	if err != nil {
		logrus.Errorf("Error fetching user attribute to trigger refresh: %v", err)
		return
	}
	now := time.Now().UTC()
	// in the case there is an invalid (or no) last refresh ignore the error, lastrefresh will be 0
	lastRefresh, _ := time.Parse(time.RFC3339, attribs.LastRefresh)
	earliestRefresh := lastRefresh.Add(r.maxAge)
	if !force && now.Before(earliestRefresh) {
		logrus.Debugf("Skipping refresh for %v due to max-age", userName)
		return
	}

	user, err := r.userLister.Get("", userName)
	if err != nil {
		logrus.Errorf("Error finding user before triggering refresh %v", err)
		return
	}

	for _, principalID := range user.PrincipalIDs {
		if strings.HasPrefix(principalID, "system://") {
			logrus.Debugf("Skipping refresh for system-user %v ", userName)
			return
		}
	}

	attribs.NeedsRefresh = true
	if needCreate {
		_, err := r.userAttributes.Create(attribs)
		if err != nil {
			logrus.Errorf("Error creating user attribute to trigger refresh: %v", err)
		}
	} else {
		_, err = r.userAttributes.Update(attribs)
		if err != nil {
			if apierrors.IsConflict(err) {
				// User attribute has just been updated, triggering the refresh.
				logrus.Debugf("Error updating user attribute to trigger refresh: %v", err)
			} else {
				logrus.Errorf("Error updating user attribute to trigger refresh: %v", err)
			}
		}
	}
}

func (r *refresher) refreshAttributes(attribs *v3.UserAttribute) (*v3.UserAttribute, error) {
	var (
		derivedTokens         []*v3.Token
		loginTokenList        []*v3.Token
		loginTokens           map[string][]*v3.Token
		canLogInAtAll         bool
		errorConfirmingLogins bool
	)

	attribs = attribs.DeepCopy()

	user, err := r.userLister.Get("", attribs.Name)
	if err != nil {
		return nil, err
	}

	loginTokens = make(map[string][]*v3.Token)

	allTokens, err := r.tokenLister.List("", labels.Everything())
	if err != nil {
		return nil, err
	}

	for providerName := range providers.ProviderNames {
		loginTokens[providerName] = []*v3.Token{}
	}

	for _, token := range allTokens {
		if token.UserID != user.Name {
			continue
		}

		if token.IsDerived {
			derivedTokens = append(derivedTokens, token)
		} else {
			loginTokens[token.AuthProvider] = append(loginTokens[token.AuthProvider], token)
			loginTokenList = append(loginTokenList, token)
		}
	}

	for providerName := range providers.ProviderNames {
		// We have to find out if the user has a userprincipal for the provider.
		prefix := providerName + "_user://"
		if providerName == "local" {
			prefix = "local://"
		}
		principalID := ""
		for _, id := range user.PrincipalIDs {
			if strings.HasPrefix(id, prefix) {
				principalID = id
				break
			}
		}
		var newGroupPrincipals []v3.Principal

		// If there is no principalID for the provider, there is no reason to go through the refetch process
		if principalID != "" {
			secret := ""
			if providers.ProvidersWithSecrets[providerName] {
				secret, err = r.tokenMGR.GetSecret(user.Name, providerName, loginTokens[providerName])
				if apierrors.IsNotFound(err) {
					// There is no secret so we can't refresh, just continue to the next attribute
					return attribs, nil
				}
				if err != nil {
					return nil, err
				}
			}

			// SAML cannot refresh, so we do restore the existing providers.
			if providers.UnrefreshableProviders[providerName] {
				existingPrincipals := attribs.GroupPrincipals[providerName].Items
				if existingPrincipals != nil {
					newGroupPrincipals = existingPrincipals
				}
			} else {
				newGroupPrincipals, err = providers.RefetchGroupPrincipals(principalID, providerName, secret)
				if err != nil {
					// In the case that we cant access a server, we still want to continue refreshing, but
					// we no longer want to disable derived tokens, or remove their login tokens for this provider
					if err.Error() != "no access" {
						errorConfirmingLogins = true
						logrus.Errorf("Error refreshing token principals, skipping: %v", err)
						existingPrincipals := attribs.GroupPrincipals[providerName].Items
						if existingPrincipals != nil {
							newGroupPrincipals = existingPrincipals
						}
						continue
					}

					// In the case that the user explicitly cannot login at all to this provider
					// (e.g. they no longer exist) we pretend they have no principal with this provider
					// so that their login tokens get blanked out
					principalID = ""

				}
			}
		}

		if len(newGroupPrincipals) == 0 {
			newGroupPrincipals = nil
		}

		attribs.GroupPrincipals[providerName] = v32.Principals{Items: newGroupPrincipals}

		canAccessProvider := false

		if principalID != "" {
			// We want to verify that the user still has rancher access
			canStillAccess, err := providers.CanAccessWithGroupProviders(providerName, principalID, newGroupPrincipals)
			if err != nil {
				return nil, err
			}

			if canStillAccess {
				canAccessProvider = true
				canLogInAtAll = true
			}
		}

		// If the user doesn't have access through this provider, we want to remove their login tokens for this provider
		if !canAccessProvider {
			for _, token := range loginTokens[providerName] {
				err := r.tokens.Delete(token.Name, &metav1.DeleteOptions{})
				if err != nil {
					if apierrors.IsNotFound(err) {
						continue
					}
					return nil, err
				}
			}
		}
	}

	// if they can still log in, or we failed to validate one of their logins, don't disable derived tokens
	if canLogInAtAll || errorConfirmingLogins {
		return attribs, nil
	}

	for _, token := range derivedTokens {
		token.Enabled = falsePointer
		_, err := r.tokenMGR.UpdateToken(token)
		if err != nil {
			return nil, err
		}
	}

	return attribs, nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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.

// A tool to add missing fields to an existing GCS bucket, for #18.
//
// Input is on stdin, and is of the form "<SHA-1> <CRC32C> <MD5>", e.g.:
//
//     e04b25d650dee1dff6ab1743724fa7c184282e94 0x12e9bf88 2bad5bb78f17232ef8c727f59eb82325
//
package main

import (
	"crypto/md5"
	"crypto/sha1"
	"flag"
	"fmt"
	"io"
	"os"

	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/gcs/gcstesting"
	"golang.org/x/net/context"
)

type crc32cChecksum uint32
type md5Hash [md5.Size]byte
type sha1Hash [sha1.Size]byte

type checksums struct {
	crc32c crc32cChecksum
	md5    md5Hash
}

// A mapping from SHA-1 to CRC32C and MD5.
type checksumMap map[sha1Hash]checksums

// Read the supplied input file, producing a checksum map.
func parseInput(in io.Reader) (m checksumMap, err error)

// List all blob objects in the GCS bucket into the channel.
func listBlobObjects(
	ctx context.Context,
	bucket gcs.Bucket,
	objects chan<- *gcs.Object) (err error)

// Filter to names of objects that lack the appropriate metadata keys.
func filterToProblematicNames(
	ctx context.Context,
	objects <-chan *gcs.Object,
	names chan<- string) (err error)

// For each object name, issue a request to set the appropriate metadata keys
// based on the contents of the supplied map. Write out the names of the
// objects processed, and those for whom info wasn't available.
func fixProblematicObjects(
	ctx context.Context,
	info checksumMap,
	names <-chan string,
	processed chan<- string,
	unknown chan<- string) (err error)

func panicIf(err *error) {
	if *err != nil {
		panic(*err)
	}
}

func run(
	bucket gcs.Bucket,
	info checksumMap) (err error)

func main() {
	flag.Parse()

	// Panic if anything below fails.
	var err error
	defer panicIf(&err)

	// Parse the input.
	info, err := parseInput(os.Stdin)
	if err != nil {
		err = fmt.Errorf("parseInput: %v", err)
		return
	}

	// Run.
	err = run(gcstesting.IntegrationTestBucketOrDie(), info)
	if err != nil {
		err = fmt.Errorf("run: %v", err)
		return
	}

	panic("TODO")
}
<commit_msg>Re-arranged a bit.<commit_after>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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.

// A tool to add missing fields to an existing GCS bucket, for #18.
//
// Input is on stdin, and is of the form "<SHA-1> <CRC32C> <MD5>", e.g.:
//
//     e04b25d650dee1dff6ab1743724fa7c184282e94 0x12e9bf88 2bad5bb78f17232ef8c727f59eb82325
//
package main

import (
	"crypto/md5"
	"crypto/sha1"
	"flag"
	"fmt"
	"io"
	"os"

	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/gcs/gcstesting"
	"golang.org/x/net/context"
)

type crc32cChecksum uint32
type md5Hash [md5.Size]byte
type sha1Hash [sha1.Size]byte

type checksums struct {
	crc32c crc32cChecksum
	md5    md5Hash
}

// A mapping from SHA-1 to CRC32C and MD5.
type checksumMap map[sha1Hash]checksums

// Read the supplied input file, producing a checksum map.
func parseInput(in io.Reader) (m checksumMap, err error)

// List all blob objects in the GCS bucket into the channel.
func listBlobObjects(
	ctx context.Context,
	bucket gcs.Bucket,
	objects chan<- *gcs.Object) (err error)

// Filter to names of objects that lack the appropriate metadata keys.
func filterToProblematicNames(
	ctx context.Context,
	objects <-chan *gcs.Object,
	names chan<- string) (err error)

// For each object name, issue a request to set the appropriate metadata keys
// based on the contents of the supplied map. Write out the names of the
// objects processed, and those for whom info wasn't available.
func fixProblematicObjects(
	ctx context.Context,
	info checksumMap,
	names <-chan string,
	processed chan<- string,
	unknown chan<- string) (err error)

func run(
	bucket gcs.Bucket,
	info checksumMap) (err error)

func panicIf(err *error) {
	if *err != nil {
		panic(*err)
	}
}

func main() {
	flag.Parse()

	// Panic if anything below fails.
	var err error
	defer panicIf(&err)

	// Parse the input.
	info, err := parseInput(os.Stdin)
	if err != nil {
		err = fmt.Errorf("parseInput: %v", err)
		return
	}

	// Run.
	err = run(gcstesting.IntegrationTestBucketOrDie(), info)
	if err != nil {
		err = fmt.Errorf("run: %v", err)
		return
	}

	panic("TODO")
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"net/http"
)

func main() {
	http.HandleFunc("/", handler)
	http.HandleFunc("/api/hello", hello)
	http.ListenAndServe(":8080", nil)
}

// Hello response structure
type Hello struct {
	Message string
}

func handler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Welcome to Cloud Native Go.")
}

func hello(w http.ResponseWriter, r *http.Request) {

	m := Hello{"Welcome to Cloud Native Go."}
	b, err := json.Marshal(m)

	if err != nil {
		panic(err)
	}

	w.Header().Add("Content-Type", "application/json; charset=utf-8")
	w.Write(b)
}
<commit_msg>Hallo Sonja added.<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"net/http"
)

func main() {
	http.HandleFunc("/", handler)
	http.HandleFunc("/api/hello", hello)
	http.ListenAndServe(":8080", nil)
}

// Hello response structure
type Hello struct {
	Message string
}

func handler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Welcome to Cloud Native Go.")
}

func hello(w http.ResponseWriter, r *http.Request) {

	m := Hello{"Welcome to Cloud Native Go. Hallo Sonja."}
	b, err := json.Marshal(m)

	if err != nil {
		panic(err)
	}

	w.Header().Add("Content-Type", "application/json; charset=utf-8")
	w.Write(b)
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 Wolfgang Johannes Kohnen <wjkohnen@users.noreply.github.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package airac

import (
	"fmt"
	"regexp"
	"strconv"
	"time"
)

// Airac represents an AIRAC cycle.
type Airac int

// Year returns the year for this AIRAC cycle's identifier.
func (a Airac) Year() int {
	return a.Effective().Year()
}

// Ordinal returns the ordinal for this AIRAC cycle's identifier.
func (a Airac) Ordinal() int {
	return (a.Effective().YearDay()-1)/28 + 1
}

// Effective returns the effective date of this AIRAC cycle.
func (a Airac) Effective() time.Time {
	return epoch.Add(time.Duration(a) * durationCycle)
}

// LongString returns a verbose representation of this AIRAC cycle.
// "YYOO (effective: YYYY-MM-DD; expires: YYYY-MM-DD)"
func (a Airac) LongString() string {
	n := a + 1
	return fmt.Sprintf("%02d%02d (effective: %s; expires: %s)",
		a.Year()%100,
		a.Ordinal(),
		a.Effective().Format(format),
		n.Effective().Add(-1).Format(format),
	)
}

// String returns a short representation of this AIRAC cycle. "YYOO"
func (a Airac) String() string {
	return fmt.Sprintf("%02d%02d", a.Year()%100, a.Ordinal())
}

// FromDate returns the AIRAC cycle that occurred at date. A date before the
// internal epoch (1901-01-10) may return wrong data. The upper limit is
// year 2192.
func FromDate(date time.Time) Airac {
	a := date.Sub(epoch) / durationCycle
	return Airac(a)
}

// FromString returns an AIRAC cycle that matches the identifier <yyoo>,
// i.e. the last two digits of the year and the ordinal, each with leading
// zeros. This works for years between 1964 and 2063. Identifiers between
// "6401" and "9913" are interpreted as AIRAC cycles between the years 1964
// and 1999 inclusive. AIRAC cycles between "0001" and "6313" are
// interpreted as AIRAC cycles between the years 2000 and 2063 inclusive.
func FromString(yyoo string) (Airac, error) {
	year, ordinal, err := parseIdentifier(yyoo)
	if err != nil {
		return -1, err
	}

	airac := FromDate(time.Date(year, time.January, 1, 0, 0, 0, 0, time.UTC))
	if airac.Year() < year {
		airac++
	}
	airac = airac + Airac(ordinal) - 1

	if airac.Year() != year {
		return -1, fmt.Errorf("year %d does not have %d airac cycles", year, ordinal)
	}

	return airac, nil
}

func parseIdentifier(yyoo string) (year, ordinal int, err error) {
	m := identifierRegex.FindStringSubmatch(yyoo)
	if m == nil {
		return -1, -1, fmt.Errorf("illegal airac identifier: %s", yyoo)
	}
	year, _ = strconv.Atoi(m[1])
	ordinal, _ = strconv.Atoi(m[2])

	if year > 63 {
		year = 1900 + year
	} else {
		year = 2000 + year
	}

	return year, ordinal, nil
}

const (
	format = "2006-01-02"
)

var (
	epoch           = time.Date(1901, time.January, 10, 0, 0, 0, 0, time.UTC)
	durationCycle   = 28 * 24 * time.Hour
	identifierRegex = regexp.MustCompile(`^(\d{2})(\d{2})$`)
)
<commit_msg>Further beautification<commit_after>/*
 * Copyright (C) 2015 Wolfgang Johannes Kohnen <wjkohnen@users.noreply.github.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package airac

import (
	"fmt"
	"regexp"
	"strconv"
	"time"
)

// Airac represents an AIRAC cycle.
type Airac int

// Year returns the year for this AIRAC cycle's identifier.
func (a Airac) Year() int {
	return a.Effective().Year()
}

// Ordinal returns the ordinal for this AIRAC cycle's identifier.
func (a Airac) Ordinal() int {
	return (a.Effective().YearDay()-1)/28 + 1
}

// Effective returns the effective date of this AIRAC cycle.
func (a Airac) Effective() time.Time {
	return epoch.Add(time.Duration(a) * durationCycle)
}

// LongString returns a verbose representation of this AIRAC cycle.
// "YYOO (effective: YYYY-MM-DD; expires: YYYY-MM-DD)"
func (a Airac) LongString() string {
	n := a + 1
	return fmt.Sprintf("%02d%02d (effective: %s; expires: %s)",
		a.Year()%100,
		a.Ordinal(),
		a.Effective().Format(format),
		n.Effective().Add(-1).Format(format),
	)
}

// String returns a short representation of this AIRAC cycle. "YYOO"
func (a Airac) String() string {
	return fmt.Sprintf("%02d%02d", a.Year()%100, a.Ordinal())
}

// FromDate returns the AIRAC cycle that occurred at date. A date before the
// internal epoch (1901-01-10) may return wrong data. The upper limit is
// year 2192.
func FromDate(date time.Time) Airac {
	a := date.Sub(epoch) / durationCycle
	return Airac(a)
}

// FromString returns an AIRAC cycle that matches the identifier <yyoo>,
// i.e. the last two digits of the year and the ordinal, each with leading
// zeros. This works for years between 1964 and 2063. Identifiers between
// "6401" and "9913" are interpreted as AIRAC cycles between the years 1964
// and 1999 inclusive. AIRAC cycles between "0001" and "6313" are
// interpreted as AIRAC cycles between the years 2000 and 2063 inclusive.
func FromString(yyoo string) (Airac, error) {
	year, ordinal, err := parseIdentifier(yyoo)
	if err != nil {
		return -1, err
	}

	lastAiracOfPreviousYear := FromDate(time.Date(year-1, time.December, 31, 0, 0, 0, 0, time.UTC))
	airac := lastAiracOfPreviousYear + Airac(ordinal)

	if airac.Year() != year {
		return -1, fmt.Errorf("year %d does not have %d airac cycles", year, ordinal)
	}

	return airac, nil
}

func parseIdentifier(yyoo string) (year, ordinal int, err error) {
	m := identifierRegex.FindStringSubmatch(yyoo)
	if m == nil {
		return -1, -1, fmt.Errorf("illegal airac identifier: %s", yyoo)
	}
	year, _ = strconv.Atoi(m[1])
	ordinal, _ = strconv.Atoi(m[2])

	if year > 63 {
		year = 1900 + year
	} else {
		year = 2000 + year
	}

	return year, ordinal, nil
}

const (
	format = "2006-01-02"
)

var (
	epoch           = time.Date(1901, time.January, 10, 0, 0, 0, 0, time.UTC)
	durationCycle   = 28 * 24 * time.Hour
	identifierRegex = regexp.MustCompile(`^(\d{2})(\d{2})$`)
)
<|endoftext|>


<commit_before>package main

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

var (
	service = flag.String("service", "", "Name of the service")
	required = flag.String("required", "", "Comma-separated list of required parameters (name:type)")
	options = flag.String("options", "", "Comma-separated list of options (name:type)")
	call = flag.String("call", "", "Call name")
	method = flag.String("method", "GET", "HTTP method (GET or POST)")
	ret = flag.String("ret", "interface{}", "Return type")
	endpoint = flag.String("endpoint", "", "Twitter RESP API endpoint")
)

func camel(s string) string {
	var news string
	for _, w := range strings.Split(s, "_") {
		news += strings.ToUpper(w[0:1]) + w[1:]
	}
	return news
}

func printComment() {
	fmt.Println("// Automatically generated")
	fmt.Printf("// %s\n", strings.Join(os.Args, " "))
}

func optSnippets() (funcs string, assigns string) {
	for _, opt := range strings.Split(*options, ",") {
		optName := strings.Split(opt, ":")[0]
		optType := strings.Split(opt, ":")[1]
		funcs += `
func (c *`+*service+*call+`Call) `+camel(optName)+`(`+optName+` `+optType+`) *`+*service+*call+`Call {
	c.opt_["`+optName+`"] = `+optName+`
	return c
}
`
		assigns += `
	if v, ok := c.opt_["`+optName+`"]; ok {
		params.Set("`+optName+`", fmt.Sprintf("%v", v))
	}
`

	}
	return
}

func printType() {

	fmt.Println(`
type `+*service+*call+`Call struct {
	s    *Service`)
	if *required != "" {
	for _, r := range strings.Split(*required, ",") {
		rName := strings.Split(r, ":")[0]
		rType := strings.Split(r, ":")[1]
		fmt.Printf("\t%s %s\n", rName, rType)
	}}
	fmt.Println("\topt_ map[string]interface{}\n}\n\n")
}

func printCall() {
	var assigns string
	if *required != "" {
	for _, r := range strings.Split(*required, ",") {
		rName := strings.Split(r, ":")[0]
		assigns += fmt.Sprintf("\tc.%s = %s\n", rName, rName)
	}}
	pars := strings.Replace(*required, ",", " ", -1)
	fmt.Println(`func (r *`+*service+`Service) `+*call+`(`+pars+`) *`+*service+*call+`Call {
	c := &`+*service+*call+`Call{s: r.s, opt_: make(map[string]interface{})}`)
	fmt.Printf("%s\treturn c\n}\n", assigns)
}

func printDo(assigns string) {
	fmt.Print(`
func (c *`+*service+*call+`Call) Do() (*`+*ret+`, os.Error) {
	var body io.Reader = nil
	params := make(url.Values)
`)

	if *required != "" {
		for _, r := range strings.Split(*required, ",") {
			rName := strings.Split(r, ":")[0]
			fmt.Printf("\tparams.Set(\"%s\", fmt.Sprintf(\"%%v\", c.%s))\n", rName, rName)
		}
	}

	fmt.Print(assigns)

	if *method == "POST" {
		fmt.Println(`
	urls := fmt.Sprintf("%s/%s.json", apiURL, "`+*endpoint+`")
	urls += "?" + params.Encode()
	body = bytes.NewBuffer([]byte(params.Encode()))
	ctype := "application/x-www-form-urlencoded"
	req, _ := http.NewRequest("POST", urls, body)
	req.Header.Set("Content-Type", ctype)
	res, err := c.s.client.Do(req)`)
	} else {
		fmt.Println(`
	urls := fmt.Sprintf("%s/%s.json", apiURL, "`+*endpoint+`")
	urls += "?" + params.Encode()
	req, _ := http.NewRequest("GET", urls, body)
	res, err := c.s.client.Do(req)`)
	}
	fmt.Println(`
	if err != nil {
		return nil, err
	}
	if err := checkResponse(res); err != nil {
		return nil, err
	}
	ret := new(`+*ret+`)
	if err := json.NewDecoder(res.Body).Decode(ret); err != nil {
		return ret, err
	}
	return ret, nil
}`)

}

func errorReq(s string) {
	fmt.Printf("Parameter '%s' is required.\n", s)
	flag.PrintDefaults()
}

func main() {

	flag.Parse()

	if *service == "" {
		errorReq("service")
		return
	}
	if *call == "" {
		errorReq("call")
		return
	}
	if *method != "GET" && *method != "POST" {
		errorReq("method (GET or POST)")
		return
	}

	funcs, assigns := optSnippets()

	printComment()
	printType()
	printCall()
	fmt.Println(funcs)
	printDo(assigns)
}
<commit_msg>Bug fixes<commit_after>package main

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

var (
	service = flag.String("service", "", "Name of the service")
	required = flag.String("required", "", "Comma-separated list of required parameters (name:type)")
	options = flag.String("options", "", "Comma-separated list of options (name:type)")
	call = flag.String("call", "", "Call name")
	method = flag.String("method", "GET", "HTTP method (GET or POST)")
	ret = flag.String("ret", "interface{}", "Return type")
	endpoint = flag.String("endpoint", "", "Twitter RESP API endpoint")
)

func camel(s string) string {
	var news string
	for _, w := range strings.Split(s, "_") {
		news += strings.ToUpper(w[0:1]) + w[1:]
	}
	return news
}

func printComment() {
	fmt.Println("// Automatically generated")
	fmt.Printf("// %s\n", strings.Join(os.Args, " "))
}

func optSnippets() (funcs string, assigns string) {
	if *options == "" {
		return "", ""
	}
	for _, opt := range strings.Split(*options, ",") {
		optName := strings.Split(opt, ":")[0]
		optType := strings.Split(opt, ":")[1]
		funcs += `
func (c *`+*service+*call+`Call) `+camel(optName)+`(`+optName+` `+optType+`) *`+*service+*call+`Call {
	c.opt_["`+optName+`"] = `+optName+`
	return c
}
`
		assigns += `
	if v, ok := c.opt_["`+optName+`"]; ok {
		params.Set("`+optName+`", fmt.Sprintf("%v", v))
	}
`

	}
	return
}

func printType() {

	fmt.Println(`
type `+*service+*call+`Call struct {
	s    *Service`)
	if *required != "" {
	for _, r := range strings.Split(*required, ",") {
		rName := strings.Split(r, ":")[0]
		rType := strings.Split(r, ":")[1]
		fmt.Printf("\t%s %s\n", rName, rType)
	}}
	fmt.Println("\topt_ map[string]interface{}\n}\n\n")
}

func printCall() {
	var assigns string
	if *required != "" {
	for _, r := range strings.Split(*required, ",") {
		rName := strings.Split(r, ":")[0]
		assigns += fmt.Sprintf("\tc.%s = %s\n", rName, rName)
	}}
	pars := strings.Replace(*required, ",", " ", -1)
	fmt.Println(`func (r *`+*service+`Service) `+*call+`(`+pars+`) *`+*service+*call+`Call {
	c := &`+*service+*call+`Call{s: r.s, opt_: make(map[string]interface{})}`)
	fmt.Printf("%s\treturn c\n}\n", assigns)
}

func printDo(assigns string) {
	fmt.Print(`
func (c *`+*service+*call+`Call) Do() (*`+*ret+`, os.Error) {
	var body io.Reader = nil
	params := make(url.Values)
`)

	if *required != "" {
		for _, r := range strings.Split(*required, ",") {
			rName := strings.Split(r, ":")[0]
			fmt.Printf("\tparams.Set(\"%s\", fmt.Sprintf(\"%%v\", c.%s))\n", rName, rName)
		}
	}

	fmt.Print(assigns)

	if *method == "POST" {
		fmt.Println(`
	urls := fmt.Sprintf("%s/%s.json", apiURL, "`+*endpoint+`")
	urls += "?" + params.Encode()
	body = bytes.NewBuffer([]byte(params.Encode()))
	ctype := "application/x-www-form-urlencoded"
	req, _ := http.NewRequest("POST", urls, body)
	req.Header.Set("Content-Type", ctype)
	res, err := c.s.client.Do(req)`)
	} else {
		fmt.Println(`
	urls := fmt.Sprintf("%s/%s.json", apiURL, "`+*endpoint+`")
	urls += "?" + params.Encode()
	req, _ := http.NewRequest("GET", urls, body)
	res, err := c.s.client.Do(req)`)
	}
	fmt.Println(`
	if err != nil {
		return nil, err
	}
	if err := checkResponse(res); err != nil {
		return nil, err
	}
	ret := new(`+*ret+`)
	if err := json.NewDecoder(res.Body).Decode(ret); err != nil {
		return ret, err
	}
	return ret, nil
}`)

}

func errorReq(s string) {
	fmt.Printf("Parameter '%s' is required.\n", s)
	flag.PrintDefaults()
}

func main() {

	flag.Parse()

	if *service == "" {
		errorReq("service")
		return
	}
	if *call == "" {
		errorReq("call")
		return
	}
	if *method != "GET" && *method != "POST" {
		errorReq("method (GET or POST)")
		return
	}

	funcs, assigns := optSnippets()

	printComment()
	printType()
	printCall()
	fmt.Println(funcs)
	printDo(assigns)
}
<|endoftext|>


<commit_before>package xesende

import (
	"encoding/xml"
	"time"
)

// Paging gives the details of the page that was accessed.
type Paging struct {
	StartIndex int
	Count      int
	TotalCount int
}

// MessagesResponse is a list of returned messages along with the paging
// information.
type SentMessagesResponse struct {
	Paging
	Messages []SentMessageResponse
}

// MessageResponse is a single sent message.
type SentMessageResponse struct {
	ID           string
	URI          string
	Reference    string
	Status       string
	LastStatusAt time.Time
	SubmittedAt  time.Time
	Type         string
	To           string
	From         string
	Summary      string
	BodyURI      string
	Direction    string
	Parts        int
	Username     string
}

// MessageResponse is a single message.
type MessageResponse struct {
	ID           string
	URI          string
	Reference    string
	Status       string
	LastStatusAt time.Time
	SubmittedAt  time.Time
	ReceivedAt   time.Time
	Type         string
	To           string
	From         string
	Summary      string
	BodyURI      string
	Direction    string
	ReadAt       time.Time
	SentAt       time.Time
	DeliveredAt  time.Time
	ReadBy       string
	Parts        int
	Username     string
}

// MessagesReceivedResponse is a list of received messages along with the paging
// information.
type ReceivedMessagesResponse struct {
	Paging
	Messages []ReceivedMessageResponse
}

// MessageReceivedResponse is a single received message.
type ReceivedMessageResponse struct {
	ID         string
	URI        string
	Reference  string
	Status     string
	ReceivedAt time.Time
	Type       string
	To         string
	From       string
	Summary    string
	BodyURI    string
	Direction  string
	Parts      int
	ReadAt     time.Time
	ReadBy     string
}

// Sent returns a list of messages sent by the user.
func (c *Client) Sent(opts ...Option) (*SentMessagesResponse, error) {
	req, err := c.newRequest("GET", "/v1.0/messageheaders", nil)
	if err != nil {
		return nil, err
	}

	for _, opt := range opts {
		opt(req)
	}

	var v messageHeadersResponse

	if _, err := c.do(req, &v); err != nil {
		return nil, err
	}

	response := &SentMessagesResponse{
		Paging: Paging{
			StartIndex: v.StartIndex,
			Count:      v.Count,
			TotalCount: v.TotalCount,
		},
		Messages: make([]SentMessageResponse, len(v.Messages)),
	}

	for i, message := range v.Messages {
		response.Messages[i] = SentMessageResponse{
			ID:           message.ID,
			URI:          message.URI,
			Reference:    message.Reference,
			Status:       message.Status,
			LastStatusAt: message.LastStatusAt.Time,
			SubmittedAt:  message.SubmittedAt.Time,
			Type:         message.Type,
			To:           message.To,
			From:         message.From,
			Summary:      message.Summary,
			BodyURI:      message.Body.URI,
			Direction:    message.Direction,
			Parts:        message.Parts,
			Username:     message.Username,
		}
	}

	return response, nil
}

// Received returns the messages sent to the user.
func (c *Client) Received(opts ...Option) (*ReceivedMessagesResponse, error) {
	req, err := c.newRequest("GET", "/v1.0/inbox/messages", nil)
	if err != nil {
		return nil, err
	}

	for _, opt := range opts {
		opt(req)
	}

	var v inboxResponse
	if _, err := c.do(req, &v); err != nil {
		return nil, err
	}

	response := &ReceivedMessagesResponse{
		Paging: Paging{
			StartIndex: v.StartIndex,
			Count:      v.Count,
			TotalCount: v.TotalCount,
		},
		Messages: make([]ReceivedMessageResponse, len(v.Messages)),
	}

	for i, message := range v.Messages {
		response.Messages[i] = ReceivedMessageResponse{
			ID:         message.ID,
			URI:        message.URI,
			Reference:  message.Reference,
			Status:     message.Status,
			ReceivedAt: message.ReceivedAt.Time,
			Type:       message.Type,
			To:         message.To,
			From:       message.From,
			Summary:    message.Summary,
			BodyURI:    message.Body.URI,
			Direction:  message.Direction,
			Parts:      message.Parts,
			ReadAt:     message.ReadAt.Time,
			ReadBy:     message.ReadBy,
		}
	}

	return response, nil
}

// Message returns the message with the given id.
func (c *Client) Message(id string) (*MessageResponse, error) {
	req, err := c.newRequest("GET", "/v1.0/messageheaders/"+id, nil)
	if err != nil {
		return nil, err
	}

	var v messageHeadersResponseMessageHeader
	if _, err = c.do(req, &v); err != nil {
		return nil, err
	}

	response := &MessageResponse{
		ID:           v.ID,
		URI:          v.URI,
		Reference:    v.Reference,
		Status:       v.Status,
		LastStatusAt: v.LastStatusAt.Time,
		SubmittedAt:  v.SubmittedAt.Time,
		ReceivedAt:   v.ReceivedAt.Time,
		Type:         v.Type,
		To:           v.To,
		From:         v.From,
		Summary:      v.Summary,
		BodyURI:      v.Body.URI,
		Direction:    v.Direction,
		ReadAt:       v.ReadAt.Time,
		SentAt:       v.SentAt.Time,
		DeliveredAt:  v.DeliveredAt.Time,
		ReadBy:       v.ReadBy,
		Parts:        v.Parts,
		Username:     v.Username,
	}

	return response, nil
}

type messageHeadersResponse struct {
	XMLName    xml.Name                              `xml:"http://api.esendex.com/ns/ messageheaders"`
	StartIndex int                                   `xml:"startindex,attr"`
	Count      int                                   `xml:"count,attr"`
	TotalCount int                                   `xml:"totalcount,attr"`
	Messages   []messageHeadersResponseMessageHeader `xml:"messageheader"`
}

type messageHeadersResponseMessageHeader struct {
	ID           string            `xml:"id,attr"`
	URI          string            `xml:"uri,attr"`
	Reference    string            `xml:"reference"`
	Status       string            `xml:"status"`
	LastStatusAt messageHeaderTime `xml:"laststatusat"`
	SubmittedAt  messageHeaderTime `xml:"submittedat"`
	ReceivedAt   messageHeaderTime `xml:"receivedat"`
	Type         string            `xml:"type"`
	To           string            `xml:"to>phonenumber"`
	From         string            `xml:"from>phonenumber"`
	Summary      string            `xml:"summary"`
	Body         struct {
		URI string `xml:"uri,attr"`
	} `xml:"body"`
	Direction   string            `xml:"direction"`
	ReadAt      messageHeaderTime `xml:"readat"`
	SentAt      messageHeaderTime `xml:"sentat"`
	DeliveredAt messageHeaderTime `xml:"deliveredat"`
	ReadBy      string            `xml:"readby"`
	Parts       int               `xml:"parts"`
	Username    string            `xml:"username"`
}

type inboxResponse struct {
	XMLName    xml.Name                     `xml:"http://api.esendex.com/ns/ messageheaders"`
	StartIndex int                          `xml:"startindex,attr"`
	Count      int                          `xml:"count,attr"`
	TotalCount int                          `xml:"totalcount,attr"`
	Messages   []inboxResponseMessageHeader `xml:"messageheader"`
}

type inboxResponseMessageHeader struct {
	ID         string            `xml:"id,attr"`
	URI        string            `xml:"uri,attr"`
	Reference  string            `xml:"reference"`
	Status     string            `xml:"status"`
	ReceivedAt messageHeaderTime `xml:"receivedat"`
	Type       string            `xml:"type"`
	To         string            `xml:"to>phonenumber"`
	From       string            `xml:"from>phonenumber"`
	Summary    string            `xml:"summary"`
	Body       struct {
		URI string `xml:"uri,attr"`
	} `xml:"body"`
	Direction string            `xml:"direction"`
	Parts     int               `xml:"parts"`
	ReadAt    messageHeaderTime `xml:"readat"`
	ReadBy    string            `xml:"readby"`
}

const messageHeaderTimeFormat = "2006-01-02T15:04:05.999999999"
const messageHeaderTimeFormatZ = "2006-01-02T15:04:05.999999999Z"

type messageHeaderTime struct {
	time.Time
}

func (t messageHeaderTime) MarshalText() ([]byte, error) {
	return []byte(t.Format(messageHeaderTimeFormat)), nil
}

func (t *messageHeaderTime) UnmarshalText(data []byte) error {
	g, err := time.ParseInLocation(messageHeaderTimeFormat, string(data), time.UTC)
	if err != nil {
		g, err = time.ParseInLocation(messageHeaderTimeFormatZ, string(data), time.UTC)
		if err != nil {
			return err
		}
	}
	*t = messageHeaderTime{g}
	return nil
}
<commit_msg>Fix documentation naming on some structs<commit_after>package xesende

import (
	"encoding/xml"
	"time"
)

// Paging gives the details of the page that was accessed.
type Paging struct {
	StartIndex int
	Count      int
	TotalCount int
}

// SentMessagesResponse is a list of returned messages along with the paging
// information.
type SentMessagesResponse struct {
	Paging
	Messages []SentMessageResponse
}

// SentMessageResponse is a single sent message.
type SentMessageResponse struct {
	ID           string
	URI          string
	Reference    string
	Status       string
	LastStatusAt time.Time
	SubmittedAt  time.Time
	Type         string
	To           string
	From         string
	Summary      string
	BodyURI      string
	Direction    string
	Parts        int
	Username     string
}

// MessageResponse is a single message.
type MessageResponse struct {
	ID           string
	URI          string
	Reference    string
	Status       string
	LastStatusAt time.Time
	SubmittedAt  time.Time
	ReceivedAt   time.Time
	Type         string
	To           string
	From         string
	Summary      string
	BodyURI      string
	Direction    string
	ReadAt       time.Time
	SentAt       time.Time
	DeliveredAt  time.Time
	ReadBy       string
	Parts        int
	Username     string
}

// ReceivedMessagesResponse is a list of received messages along with the paging
// information.
type ReceivedMessagesResponse struct {
	Paging
	Messages []ReceivedMessageResponse
}

// ReceivedMessageResponse is a single received message.
type ReceivedMessageResponse struct {
	ID         string
	URI        string
	Reference  string
	Status     string
	ReceivedAt time.Time
	Type       string
	To         string
	From       string
	Summary    string
	BodyURI    string
	Direction  string
	Parts      int
	ReadAt     time.Time
	ReadBy     string
}

// Sent returns a list of messages sent by the user.
func (c *Client) Sent(opts ...Option) (*SentMessagesResponse, error) {
	req, err := c.newRequest("GET", "/v1.0/messageheaders", nil)
	if err != nil {
		return nil, err
	}

	for _, opt := range opts {
		opt(req)
	}

	var v messageHeadersResponse

	if _, err := c.do(req, &v); err != nil {
		return nil, err
	}

	response := &SentMessagesResponse{
		Paging: Paging{
			StartIndex: v.StartIndex,
			Count:      v.Count,
			TotalCount: v.TotalCount,
		},
		Messages: make([]SentMessageResponse, len(v.Messages)),
	}

	for i, message := range v.Messages {
		response.Messages[i] = SentMessageResponse{
			ID:           message.ID,
			URI:          message.URI,
			Reference:    message.Reference,
			Status:       message.Status,
			LastStatusAt: message.LastStatusAt.Time,
			SubmittedAt:  message.SubmittedAt.Time,
			Type:         message.Type,
			To:           message.To,
			From:         message.From,
			Summary:      message.Summary,
			BodyURI:      message.Body.URI,
			Direction:    message.Direction,
			Parts:        message.Parts,
			Username:     message.Username,
		}
	}

	return response, nil
}

// Received returns the messages sent to the user.
func (c *Client) Received(opts ...Option) (*ReceivedMessagesResponse, error) {
	req, err := c.newRequest("GET", "/v1.0/inbox/messages", nil)
	if err != nil {
		return nil, err
	}

	for _, opt := range opts {
		opt(req)
	}

	var v inboxResponse
	if _, err := c.do(req, &v); err != nil {
		return nil, err
	}

	response := &ReceivedMessagesResponse{
		Paging: Paging{
			StartIndex: v.StartIndex,
			Count:      v.Count,
			TotalCount: v.TotalCount,
		},
		Messages: make([]ReceivedMessageResponse, len(v.Messages)),
	}

	for i, message := range v.Messages {
		response.Messages[i] = ReceivedMessageResponse{
			ID:         message.ID,
			URI:        message.URI,
			Reference:  message.Reference,
			Status:     message.Status,
			ReceivedAt: message.ReceivedAt.Time,
			Type:       message.Type,
			To:         message.To,
			From:       message.From,
			Summary:    message.Summary,
			BodyURI:    message.Body.URI,
			Direction:  message.Direction,
			Parts:      message.Parts,
			ReadAt:     message.ReadAt.Time,
			ReadBy:     message.ReadBy,
		}
	}

	return response, nil
}

// Message returns the message with the given id.
func (c *Client) Message(id string) (*MessageResponse, error) {
	req, err := c.newRequest("GET", "/v1.0/messageheaders/"+id, nil)
	if err != nil {
		return nil, err
	}

	var v messageHeadersResponseMessageHeader
	if _, err = c.do(req, &v); err != nil {
		return nil, err
	}

	response := &MessageResponse{
		ID:           v.ID,
		URI:          v.URI,
		Reference:    v.Reference,
		Status:       v.Status,
		LastStatusAt: v.LastStatusAt.Time,
		SubmittedAt:  v.SubmittedAt.Time,
		ReceivedAt:   v.ReceivedAt.Time,
		Type:         v.Type,
		To:           v.To,
		From:         v.From,
		Summary:      v.Summary,
		BodyURI:      v.Body.URI,
		Direction:    v.Direction,
		ReadAt:       v.ReadAt.Time,
		SentAt:       v.SentAt.Time,
		DeliveredAt:  v.DeliveredAt.Time,
		ReadBy:       v.ReadBy,
		Parts:        v.Parts,
		Username:     v.Username,
	}

	return response, nil
}

type messageHeadersResponse struct {
	XMLName    xml.Name                              `xml:"http://api.esendex.com/ns/ messageheaders"`
	StartIndex int                                   `xml:"startindex,attr"`
	Count      int                                   `xml:"count,attr"`
	TotalCount int                                   `xml:"totalcount,attr"`
	Messages   []messageHeadersResponseMessageHeader `xml:"messageheader"`
}

type messageHeadersResponseMessageHeader struct {
	ID           string            `xml:"id,attr"`
	URI          string            `xml:"uri,attr"`
	Reference    string            `xml:"reference"`
	Status       string            `xml:"status"`
	LastStatusAt messageHeaderTime `xml:"laststatusat"`
	SubmittedAt  messageHeaderTime `xml:"submittedat"`
	ReceivedAt   messageHeaderTime `xml:"receivedat"`
	Type         string            `xml:"type"`
	To           string            `xml:"to>phonenumber"`
	From         string            `xml:"from>phonenumber"`
	Summary      string            `xml:"summary"`
	Body         struct {
		URI string `xml:"uri,attr"`
	} `xml:"body"`
	Direction   string            `xml:"direction"`
	ReadAt      messageHeaderTime `xml:"readat"`
	SentAt      messageHeaderTime `xml:"sentat"`
	DeliveredAt messageHeaderTime `xml:"deliveredat"`
	ReadBy      string            `xml:"readby"`
	Parts       int               `xml:"parts"`
	Username    string            `xml:"username"`
}

type inboxResponse struct {
	XMLName    xml.Name                     `xml:"http://api.esendex.com/ns/ messageheaders"`
	StartIndex int                          `xml:"startindex,attr"`
	Count      int                          `xml:"count,attr"`
	TotalCount int                          `xml:"totalcount,attr"`
	Messages   []inboxResponseMessageHeader `xml:"messageheader"`
}

type inboxResponseMessageHeader struct {
	ID         string            `xml:"id,attr"`
	URI        string            `xml:"uri,attr"`
	Reference  string            `xml:"reference"`
	Status     string            `xml:"status"`
	ReceivedAt messageHeaderTime `xml:"receivedat"`
	Type       string            `xml:"type"`
	To         string            `xml:"to>phonenumber"`
	From       string            `xml:"from>phonenumber"`
	Summary    string            `xml:"summary"`
	Body       struct {
		URI string `xml:"uri,attr"`
	} `xml:"body"`
	Direction string            `xml:"direction"`
	Parts     int               `xml:"parts"`
	ReadAt    messageHeaderTime `xml:"readat"`
	ReadBy    string            `xml:"readby"`
}

const messageHeaderTimeFormat = "2006-01-02T15:04:05.999999999"
const messageHeaderTimeFormatZ = "2006-01-02T15:04:05.999999999Z"

type messageHeaderTime struct {
	time.Time
}

func (t messageHeaderTime) MarshalText() ([]byte, error) {
	return []byte(t.Format(messageHeaderTimeFormat)), nil
}

func (t *messageHeaderTime) UnmarshalText(data []byte) error {
	g, err := time.ParseInLocation(messageHeaderTimeFormat, string(data), time.UTC)
	if err != nil {
		g, err = time.ParseInLocation(messageHeaderTimeFormatZ, string(data), time.UTC)
		if err != nil {
			return err
		}
	}
	*t = messageHeaderTime{g}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"sync"
	"time"

	"github.com/mdigger/log"
	"github.com/mdigger/mx"
)

// MonitorStart запускает пользовательские мониторы для указанных пользователей.
// Не существующие пользователи "молча" игнорируются. Так что единственная
// ошибка, которая может возвратиться, это в случае проблем с соединением с MX.
func (s *MXServer) MonitorStart(jids ...mx.JID) error {
	for _, jid := range jids {
		var contact = s.contacts.Get(jid)
		if contact == nil {
			continue // неизвестный нам уникальный идентификатор пользователя
		}
		// запускаем пользовательский монитор
		id, err := s.conn.MonitorStart(contact.Ext)
		if err != nil {
			return err
		}
		// сохраняем идентификатор пользователя монитора
		s.monitors.Store(id, jid)
		s.log.WithFields(log.Fields{
			"jid":     jid,
			"ext":     contact.Ext,
			"monitor": id,
		}).Debug("user monitor started")
	}
	return nil
}

// MonitorStop останавливает мониторы для указанных пользователей.
// Если монитор пользователя с каким-то идентификатором не запущен, то не
// страшно - его отсечет обработчик остановки мониторов. Так что единственная
// ошибка, которая может вернуться, это ошибка соединения с MX.
func (s *MXServer) MonitorStop(jids ...mx.JID) (err error) {
	for _, jid := range jids {
		var id = s.monitors.Get(jid)
		if id == 0 {
			continue
		}
		s.monitors.Delete(id)
		if err = s.conn.MonitorStop(id); err != nil {
			return err
		}
		s.log.WithFields(log.Fields{
			"jid":     jid,
			"monitor": id,
		}).Debug("user monitor stoped")
	}
	return nil
}

// CallMonitor запускает мониторинг входящих звонков. Возвращает ошибку в
// случае ошибки соединения.
func (s *MXServer) CallMonitor(
	sendPush func(userID string, payload interface{}) error) error {
	s.log.Debug("calls monitor started")
	var err = s.conn.Handle(func(resp *mx.Response) error {
		var delivery = new(Delivery)
		if err := resp.Decode(delivery); err != nil {
			return err
		}
		if delivery.CalledDevice == "" {
			return nil
		}
		delivery.Timestamp = time.Now().Unix()
		var jid = s.monitors.GetJID(delivery.MonitorCrossRefID)
		if jid == 0 {
			s.log.WithField("monitor", delivery.MonitorCrossRefID).
				Warning("monitor not found")
			return nil
		}
		s.log.WithFields(log.Fields{
			"monitor": delivery.MonitorCrossRefID,
			"callId":  delivery.CallID,
			"jid":     jid,
		}).Debug("incoming call")
		var userID = fmt.Sprintf("%s:%s", s.ID(), jid)
		// отправляем уведомление на устройства пользователя
		sendPush(userID, delivery)
		return nil
	}, "DeliveredEvent")
	s.log.Debug("calls monitor stoped")
	return err
}

// Delivery описывает структуру события входящего звонка
type Delivery struct {
	MonitorCrossRefID     int64  `xml:"monitorCrossRefID" json:"-"`
	CallID                int64  `xml:"connection>callID" json:"callId"`
	DeviceID              string `xml:"connection>deviceID" json:"deviceId"`
	GlobalCallID          string `xml:"connection>globalCallID" json:"globalCallId"`
	CallingDevice         string `xml:"callingDevice>deviceIdentifier" json:"callingDevice"`
	CalledDevice          string `xml:"calledDevice>deviceIdentifier" json:"calledDevice"`
	AlertingDevice        string `xml:"alertingDevice>deviceIdentifier" json:"alertingDevice"`
	LastRedirectionDevice string `xml:"lastRedirectionDevice>deviceIdentifier" json:"lastRedirectionDevice"`
	LocalConnectionInfo   string `xml:"localConnectionInfo" json:"localConnectionInfo"`
	Cause                 string `xml:"cause" json:"cause"`
	CallTypeFlags         int64  `xml:"callTypeFlags" json:"callTypeFlags,omitempty"`
	Timestamp             int64  `xml:"-" json:"timestamp"`
}

// Monitors хранит ассоциацию номера запущенного монитора пользователя и
// его уникального идентификатора.
type Monitors struct {
	monitors sync.Map
}

// Store регистрирует ассоциацию номера монитора и идентификатора пользователя.
func (m *Monitors) Store(id int64, jid mx.JID) {
	m.monitors.Store(id, jid)
}

// Delete удаляет запись по номеру монитора.
func (m *Monitors) Delete(id int64) {
	m.monitors.Delete(id)
}

// Get возвращает номер монитора по идентификатору пользователя. Если с
// пользователем не связан ни один монитор, то возвращается 0.
func (m *Monitors) Get(jid mx.JID) int64 {
	var monitor int64
	m.monitors.Range(func(id, jid2 interface{}) bool {
		monitor = id.(int64)
		return jid == jid2.(mx.JID)
	})
	return monitor
}

// GetJID возвращает уникальный идентификатор пользователя по номеру монитора.
// Если пользователь не привязан к монитору, то возвращается 0.
func (m *Monitors) GetJID(id int64) mx.JID {
	if jid, ok := m.monitors.Load(id); ok {
		return jid.(mx.JID)
	}
	return 0
}
<commit_msg>monitors fix<commit_after>package main

import (
	"fmt"
	"sync"
	"time"

	"github.com/mdigger/log"
	"github.com/mdigger/mx"
)

// MonitorStart запускает пользовательские мониторы для указанных пользователей.
// Не существующие пользователи "молча" игнорируются. Так что единственная
// ошибка, которая может возвратиться, это в случае проблем с соединением с MX.
func (s *MXServer) MonitorStart(jids ...mx.JID) error {
	for _, jid := range jids {
		var contact = s.contacts.Get(jid)
		if contact == nil {
			continue // неизвестный нам уникальный идентификатор пользователя
		}
		// проверяем, что монитор еще не запущен
		if s.monitors.Get(jid) != 0 {
			continue // монитор уже запущен
		}
		// запускаем пользовательский монитор
		id, err := s.conn.MonitorStart(contact.Ext)
		if err != nil {
			return err
		}
		// сохраняем идентификатор пользователя монитора
		s.monitors.Store(jid, id)
		s.log.WithFields(log.Fields{
			"jid":     jid,
			"ext":     contact.Ext,
			"monitor": id,
		}).Debug("user monitor started")
	}
	return nil
}

// MonitorStop останавливает мониторы для указанных пользователей.
// Если монитор пользователя с каким-то идентификатором не запущен, то не
// страшно - его отсечет обработчик остановки мониторов. Так что единственная
// ошибка, которая может вернуться, это ошибка соединения с MX.
func (s *MXServer) MonitorStop(jids ...mx.JID) (err error) {
	for _, jid := range jids {
		var id = s.monitors.Get(jid)
		if id == 0 {
			continue
		}
		s.monitors.Delete(jid)
		if err = s.conn.MonitorStop(id); err != nil {
			return err
		}
		s.log.WithFields(log.Fields{
			"jid":     jid,
			"monitor": id,
		}).Debug("user monitor stoped")
	}
	return nil
}

// CallMonitor запускает мониторинг входящих звонков. Возвращает ошибку в
// случае ошибки соединения.
func (s *MXServer) CallMonitor(
	sendPush func(userID string, payload interface{}) error) error {
	s.log.Debug("calls monitor started")
	var err = s.conn.Handle(func(resp *mx.Response) error {
		var delivery = new(Delivery)
		if err := resp.Decode(delivery); err != nil {
			return err
		}
		if delivery.CalledDevice == "" {
			return nil
		}
		delivery.Timestamp = time.Now().Unix()
		var jid = s.monitors.GetJID(delivery.MonitorCrossRefID)
		if jid == 0 {
			s.log.WithField("monitor", delivery.MonitorCrossRefID).
				Warning("monitor not found")
			return nil
		}
		s.log.WithFields(log.Fields{
			"monitor": delivery.MonitorCrossRefID,
			"callId":  delivery.CallID,
			"jid":     jid,
		}).Debug("incoming call")
		var userID = fmt.Sprintf("%s:%s", s.ID(), jid)
		// отправляем уведомление на устройства пользователя
		sendPush(userID, delivery)
		return nil
	}, "DeliveredEvent")
	s.log.Debug("calls monitor stoped")
	return err
}

// Delivery описывает структуру события входящего звонка
type Delivery struct {
	MonitorCrossRefID     int64  `xml:"monitorCrossRefID" json:"-"`
	CallID                int64  `xml:"connection>callID" json:"callId"`
	DeviceID              string `xml:"connection>deviceID" json:"deviceId"`
	GlobalCallID          string `xml:"connection>globalCallID" json:"globalCallId"`
	CallingDevice         string `xml:"callingDevice>deviceIdentifier" json:"callingDevice"`
	CalledDevice          string `xml:"calledDevice>deviceIdentifier" json:"calledDevice"`
	AlertingDevice        string `xml:"alertingDevice>deviceIdentifier" json:"alertingDevice"`
	LastRedirectionDevice string `xml:"lastRedirectionDevice>deviceIdentifier" json:"lastRedirectionDevice"`
	LocalConnectionInfo   string `xml:"localConnectionInfo" json:"localConnectionInfo"`
	Cause                 string `xml:"cause" json:"cause"`
	CallTypeFlags         int64  `xml:"callTypeFlags" json:"callTypeFlags,omitempty"`
	Timestamp             int64  `xml:"-" json:"timestamp"`
}

// Monitors хранит ассоциацию номера запущенного монитора пользователя и
// его уникального идентификатора.
type Monitors struct {
	monitors sync.Map // jid -> monitor-id
}

// Store регистрирует ассоциацию идентификатора пользователя и номера монитора.
func (m *Monitors) Store(jid mx.JID, id int64) {
	m.monitors.Store(jid, id)
}

// Delete удаляет запись о мониторе по идентификатору пользователя.
func (m *Monitors) Delete(jid mx.JID) {
	m.monitors.Delete(jid)
}

// Get возвращает номер монитора по идентификатору пользователя. Если с
// пользователем не связан ни один монитор, то возвращается 0.
func (m *Monitors) Get(jid mx.JID) int64 {
	if id, ok := m.monitors.Load(jid); ok {
		return id.(int64)
	}
	return 0
}

// GetJID возвращает уникальный идентификатор пользователя по номеру монитора.
// Если пользователь не привязан к монитору, то возвращается 0.
func (m *Monitors) GetJID(id int64) mx.JID {
	var jid mx.JID
	m.monitors.Range(func(jid2, id2 interface{}) bool {
		if id != id2.(int64) {
			return true
		}
		jid = jid2.(mx.JID)
		return false
	})
	return jid
}
<|endoftext|>


<commit_before>package sampi

import (
	"strconv"

	"github.com/titanous/go-dockerclient"
)

type Job struct {
	ID string

	// The ID of the container to run
	Container string
	// Job attributes (all host rules must match successfully)
	Attributes map[string]string
	// Resource requirements (decremented from host resources)
	Resources map[string]int

	Config *docker.Config
}

type ResourceValue struct {
	Value      int
	Overcommit bool
}

type Host struct {
	ID string

	// TODO: host properties/attributes?

	// Currently running jobs
	Jobs []*Job
	// All rules must match job attributes
	Rules []Rule
	// Currently available resources
	Resources map[string]ResourceValue
}

func (h *Host) Compatible(job *Job) bool {
	for _, r := range h.Rules {
		if !r.Match(job.Attributes) {
			return false
		}
	}
	return true
}

func (h *Host) Fits(job *Job) bool {
	for k, v := range job.Resources {
		res, ok := h.Resources[k]
		if !ok || !res.Overcommit && res.Value < v {
			return false
		}
	}
	return true
}

func (h *Host) Add(job *Job) bool {
	if !h.Compatible(job) {
		return false
	}
	for k, v := range job.Resources {
		res, ok := h.Resources[k]
		if !ok || !res.Overcommit && res.Value < v {
			return false
		}
		res.Value -= v
		h.Resources[k] = res
	}
	h.Jobs = append(h.Jobs, job)
	return true
}

type ScheduleReq struct {
	// If true, commit all jobs that fit; if false, reject entire request if a single job doesn't fit
	Incremental bool
	// map of host id -> new jobs
	HostJobs map[string][]*Job
}

type ScheduleRes struct {
	// The state of the cluster after the operation
	State map[string]Host
	// If the request was incremental, the jobs that were not scheduled
	RemainingJobs []*Job
	// true if the request was atomic and all jobs were committed
	Success bool
}

type RuleOperator int

const (
	OpEq RuleOperator = iota
	OpNotEq
	OpGt
	OpGtEq
	OpLt
	OpLtEq
)

type Rule struct {
	Key   string
	Op    RuleOperator
	Value string
}

func (r *Rule) Match(h map[string]string) bool {
	v, ok := h[r.Key]
	if !ok {
		return r.Op == OpEq && r.Value == "nil"
	}
	switch r.Op {
	case OpEq:
		return r.Value == v
	case OpNotEq:
		return r.Value != v
	}

	// TODO: cache these somewhere
	left, _ := strconv.Atoi(v)
	right, _ := strconv.Atoi(r.Value)
	switch r.Op {
	case OpGt:
		return left > right
	case OpGtEq:
		return left >= right
	case OpLt:
		return left < right
	case OpLtEq:
		return left <= right
	default:
		// invalid op
		return false
	}
}
<commit_msg>host/sampi: Add Job.TCPPorts<commit_after>package sampi

import (
	"strconv"

	"github.com/titanous/go-dockerclient"
)

type Job struct {
	ID string

	// The ID of the container to run
	Container string
	// Job attributes (all host rules must match successfully)
	Attributes map[string]string
	// Resource requirements (decremented from host resources)
	Resources map[string]int
	// Number of TCP ports required by the job (current max: 1)
	// TODO: move to Attrs/Resources?
	TCPPorts int

	Config *docker.Config
}

type ResourceValue struct {
	Value      int
	Overcommit bool
}

type Host struct {
	ID string

	// TODO: host properties/attributes?

	// Currently running jobs
	Jobs []*Job
	// All rules must match job attributes
	Rules []Rule
	// Currently available resources
	Resources map[string]ResourceValue
}

func (h *Host) Compatible(job *Job) bool {
	for _, r := range h.Rules {
		if !r.Match(job.Attributes) {
			return false
		}
	}
	return true
}

func (h *Host) Fits(job *Job) bool {
	for k, v := range job.Resources {
		res, ok := h.Resources[k]
		if !ok || !res.Overcommit && res.Value < v {
			return false
		}
	}
	return true
}

func (h *Host) Add(job *Job) bool {
	if !h.Compatible(job) {
		return false
	}
	for k, v := range job.Resources {
		res, ok := h.Resources[k]
		if !ok || !res.Overcommit && res.Value < v {
			return false
		}
		res.Value -= v
		h.Resources[k] = res
	}
	h.Jobs = append(h.Jobs, job)
	return true
}

type ScheduleReq struct {
	// If true, commit all jobs that fit; if false, reject entire request if a single job doesn't fit
	Incremental bool
	// map of host id -> new jobs
	HostJobs map[string][]*Job
}

type ScheduleRes struct {
	// The state of the cluster after the operation
	State map[string]Host
	// If the request was incremental, the jobs that were not scheduled
	RemainingJobs []*Job
	// true if the request was atomic and all jobs were committed
	Success bool
}

type RuleOperator int

const (
	OpEq RuleOperator = iota
	OpNotEq
	OpGt
	OpGtEq
	OpLt
	OpLtEq
)

type Rule struct {
	Key   string
	Op    RuleOperator
	Value string
}

func (r *Rule) Match(h map[string]string) bool {
	v, ok := h[r.Key]
	if !ok {
		return r.Op == OpEq && r.Value == "nil"
	}
	switch r.Op {
	case OpEq:
		return r.Value == v
	case OpNotEq:
		return r.Value != v
	}

	// TODO: cache these somewhere
	left, _ := strconv.Atoi(v)
	right, _ := strconv.Atoi(r.Value)
	switch r.Op {
	case OpGt:
		return left > right
	case OpGtEq:
		return left >= right
	case OpLt:
		return left < right
	case OpLtEq:
		return left <= right
	default:
		// invalid op
		return false
	}
}
<|endoftext|>


<commit_before>package switchboard

import (
	"errors"
	"fmt"
	"net"

	"github.com/pivotal-golang/lager"
)

type Backend interface {
	HealthcheckUrl() string
	Bridge(clientConn net.Conn) error
	Dial() (net.Conn, error)
	SeverConnections()
}

type backend struct {
	ipAddress       string
	port            uint
	healthcheckPort uint
	logger          lager.Logger
	bridges         Bridges
}

func NewBackend(ipAddress string, port uint, healthcheckPort uint, logger lager.Logger) Backend {
	return &backend{
		ipAddress:       ipAddress,
		port:            port,
		healthcheckPort: healthcheckPort,
		logger:          logger,
		bridges:         NewBridges(),
	}
}

func (b backend) HealthcheckUrl() string {
	endpoint := fmt.Sprintf("http://%s:%d", b.ipAddress, b.healthcheckPort)
	return endpoint
}

func (b backend) Bridge(clientConn net.Conn) error {
	backendConn, err := b.Dial()
	if err != nil {
		return errors.New(fmt.Sprintf("Error connection to backend: %v", err))
	}

	bridge := NewConnectionBridge(clientConn, backendConn, b.logger)
	b.bridges.Add(bridge)

	go func() {
		bridge.Connect()
		b.bridges.Remove(bridge)
	}()

	return nil
}

func (b backend) Dial() (net.Conn, error) {
	addr := fmt.Sprintf("%s:%d", b.ipAddress, b.port)
	backendConn, err := net.Dial("tcp", addr)
	if err != nil {
		return nil, err
	}
	return backendConn, nil
}

func (b backend) SeverConnections() {
	b.bridges.RemoveAndCloseAll()
}
<commit_msg>Encapsulate/hide Backend.dial implementation detail<commit_after>package switchboard

import (
	"errors"
	"fmt"
	"net"

	"github.com/pivotal-golang/lager"
)

type Backend interface {
	HealthcheckUrl() string
	Bridge(clientConn net.Conn) error
	SeverConnections()
}

type backend struct {
	ipAddress       string
	port            uint
	healthcheckPort uint
	logger          lager.Logger
	bridges         Bridges
}

func NewBackend(ipAddress string, port uint, healthcheckPort uint, logger lager.Logger) Backend {
	return &backend{
		ipAddress:       ipAddress,
		port:            port,
		healthcheckPort: healthcheckPort,
		logger:          logger,
		bridges:         NewBridges(),
	}
}

func (b backend) HealthcheckUrl() string {
	endpoint := fmt.Sprintf("http://%s:%d", b.ipAddress, b.healthcheckPort)
	return endpoint
}

func (b backend) Bridge(clientConn net.Conn) error {
	backendConn, err := b.dial()
	if err != nil {
		return errors.New(fmt.Sprintf("Error connection to backend: %v", err))
	}

	bridge := NewConnectionBridge(clientConn, backendConn, b.logger)
	b.bridges.Add(bridge)

	go func() {
		bridge.Connect()
		b.bridges.Remove(bridge)
	}()

	return nil
}

func (b backend) SeverConnections() {
	b.bridges.RemoveAndCloseAll()
}

func (b backend) dial() (net.Conn, error) {
	addr := fmt.Sprintf("%s:%d", b.ipAddress, b.port)
	backendConn, err := net.Dial("tcp", addr)
	if err != nil {
		return nil, err
	}
	return backendConn, nil
}
<|endoftext|>


<commit_before>package kafka_output

import (
	"encoding/json"
	"log"
	"os"
	"strings"
	"time"

	"github.com/Shopify/sarama"
	"github.com/google/uuid"
	"github.com/karimra/gnmic/outputs"
	"github.com/mitchellh/mapstructure"
	"github.com/prometheus/client_golang/prometheus"
)

const (
	defaultKafkaMaxRetry = 2
	defaultKafkaTimeout  = 5
	defaultKafkaTopic    = "telemetry"
)

func init() {
	outputs.Register("kafka", func() outputs.Output {
		return &KafkaOutput{
			Cfg: &Config{},
		}
	})
}

// KafkaOutput //
type KafkaOutput struct {
	Cfg      *Config
	producer sarama.SyncProducer
	metrics  []prometheus.Collector
	logger   sarama.StdLogger
}

// Config //
type Config struct {
	Address  string `mapstructure:"address,omitempty"`
	Topic    string `mapstructure:"topic,omitempty"`
	Name     string `mapstructure:"name,omitempty"`
	MaxRetry int    `mapstructure:"max-retry,omitempty"`
	Timeout  int    `mapstructure:"timeout,omitempty"`
}

func (k *KafkaOutput) String() string {
	b, err := json.Marshal(k)
	if err != nil {
		return ""
	}
	return string(b)
}

// Init /
func (k *KafkaOutput) Init(cfg map[string]interface{}, logger *log.Logger) error {
	err := mapstructure.Decode(cfg, k.Cfg)
	if err != nil {
		return err
	}
	if k.Cfg.Topic == "" {
		k.Cfg.Topic = defaultKafkaTopic
	}
	if k.Cfg.MaxRetry == 0 {
		k.Cfg.MaxRetry = defaultKafkaMaxRetry
	}
	if k.Cfg.Timeout == 0 {
		k.Cfg.Timeout = defaultKafkaTimeout
	}
	if logger != nil {
		sarama.Logger = log.New(logger.Writer(), "kafka_output ", logger.Flags())
	} else {
		sarama.Logger = log.New(os.Stderr, "kafka_output ", log.LstdFlags|log.Lmicroseconds)
	}
	k.logger = sarama.Logger
	config := sarama.NewConfig()
	if k.Cfg.Name != "" {
		config.ClientID = k.Cfg.Name
	} else {
		config.ClientID = "gnmic-" + uuid.New().String()
	}

	config.Producer.Retry.Max = k.Cfg.MaxRetry
	config.Producer.RequiredAcks = sarama.WaitForAll
	config.Producer.Return.Successes = true
	config.Producer.Timeout = time.Duration(k.Cfg.Timeout) * time.Second
	k.producer, err = sarama.NewSyncProducer(strings.Split(k.Cfg.Address, ","), config)
	if err != nil {
		return err
	}
	k.logger.Printf("initialized kafka producer: %s", k.String())
	return err
}

// Write //
func (k *KafkaOutput) Write(b []byte, meta outputs.Meta) {
	if len(b) == 0 {
		return
	}
	if format, ok := meta["format"]; ok {
		if format == "textproto" {
			return
		}
	}
	msg := &sarama.ProducerMessage{
		Topic: k.Cfg.Topic,
		Value: sarama.ByteEncoder(b),
	}
	_, _, err := k.producer.SendMessage(msg)
	if err != nil {
		k.logger.Printf("failed to send a kafka msg to topic '%s': %v", k.Cfg.Topic, err)
	}
	// 	k.logger.Printf("wrote %d bytes to kafka_topic=%s", len(b), k.Cfg.Topic)
}

// Close //
func (k *KafkaOutput) Close() error {
	return k.producer.Close()
}

// Metrics //
func (k *KafkaOutput) Metrics() []prometheus.Collector { return k.metrics }
<commit_msg>update kafka output<commit_after>package kafka_output

import (
	"encoding/json"
	"log"
	"os"
	"strings"
	"time"

	"github.com/Shopify/sarama"
	"github.com/google/uuid"
	"github.com/karimra/gnmic/collector"
	"github.com/karimra/gnmic/outputs"
	"github.com/mitchellh/mapstructure"
	"github.com/openconfig/gnmi/proto/gnmi"
	"github.com/prometheus/client_golang/prometheus"
	"google.golang.org/protobuf/encoding/protojson"
	"google.golang.org/protobuf/proto"
)

const (
	defaultKafkaMaxRetry = 2
	defaultKafkaTimeout  = 5
	defaultKafkaTopic    = "telemetry"
)

func init() {
	outputs.Register("kafka", func() outputs.Output {
		return &KafkaOutput{
			Cfg: &Config{},
		}
	})
}

// KafkaOutput //
type KafkaOutput struct {
	Cfg      *Config
	producer sarama.SyncProducer
	metrics  []prometheus.Collector
	logger   sarama.StdLogger
}

// Config //
type Config struct {
	Address  string `mapstructure:"address,omitempty"`
	Topic    string `mapstructure:"topic,omitempty"`
	Name     string `mapstructure:"name,omitempty"`
	MaxRetry int    `mapstructure:"max-retry,omitempty"`
	Timeout  int    `mapstructure:"timeout,omitempty"`
	Format   string `mapstructure:"format,omitempty"`
}

func (k *KafkaOutput) String() string {
	b, err := json.Marshal(k)
	if err != nil {
		return ""
	}
	return string(b)
}

// Init /
func (k *KafkaOutput) Init(cfg map[string]interface{}, logger *log.Logger) error {
	err := mapstructure.Decode(cfg, k.Cfg)
	if err != nil {
		return err
	}
	if k.Cfg.Topic == "" {
		k.Cfg.Topic = defaultKafkaTopic
	}
	if k.Cfg.MaxRetry == 0 {
		k.Cfg.MaxRetry = defaultKafkaMaxRetry
	}
	if k.Cfg.Timeout == 0 {
		k.Cfg.Timeout = defaultKafkaTimeout
	}
	if logger != nil {
		sarama.Logger = log.New(logger.Writer(), "kafka_output ", logger.Flags())
	} else {
		sarama.Logger = log.New(os.Stderr, "kafka_output ", log.LstdFlags|log.Lmicroseconds)
	}
	k.logger = sarama.Logger
	config := sarama.NewConfig()
	if k.Cfg.Name != "" {
		config.ClientID = k.Cfg.Name
	} else {
		config.ClientID = "gnmic-" + uuid.New().String()
	}

	config.Producer.Retry.Max = k.Cfg.MaxRetry
	config.Producer.RequiredAcks = sarama.WaitForAll
	config.Producer.Return.Successes = true
	config.Producer.Timeout = time.Duration(k.Cfg.Timeout) * time.Second
	k.producer, err = sarama.NewSyncProducer(strings.Split(k.Cfg.Address, ","), config)
	if err != nil {
		return err
	}
	k.logger.Printf("initialized kafka producer: %s", k.String())
	return err
}

// Write //
func (k *KafkaOutput) Write(rsp proto.Message, meta outputs.Meta) {
	if rsp == nil {
		return
	}
	if format, ok := meta["format"]; ok {
		if format == "textproto" {
			return
		}
	}
	b := make([]byte, 0)
	var err error
	switch k.Cfg.Format {
	case "proto":
		b, err = proto.Marshal(rsp)
	case "json":
		b, err = protojson.Marshal(rsp)
	case "event":
		switch sub := rsp.ProtoReflect().Interface().(type) {
		case *gnmi.SubscribeResponse:
			var subscriptionName string
			var ok bool
			if subscriptionName, ok = meta["subscription-name"]; !ok {
				subscriptionName = "default"
			}
			switch sub.Response.(type) {
			case *gnmi.SubscribeResponse_Update:
				events, err := collector.ResponseToEventMsgs(subscriptionName, sub, meta)
				if err != nil {
					k.logger.Printf("failed converting response to events: %v", err)
					return
				}
				b, err = json.Marshal(events)
				if err != nil {
					k.logger.Printf("failed marshaling events: %v", err)
					return
				}
			case *gnmi.SubscribeResponse_SyncResponse:
				k.logger.Printf("received subscribe syncResponse with %v", meta)
			case *gnmi.SubscribeResponse_Error:
				gnmiErr := sub.GetError()
				k.logger.Printf("received subscribe response error with %v, code=%d, message=%v, data=%v ",
					meta, gnmiErr.Code, gnmiErr.Message, gnmiErr.Data)
			}
		}
	}
	if err != nil {
		k.logger.Printf("failed marshaling event: %v", err)
		return
	}
	msg := &sarama.ProducerMessage{
		Topic: k.Cfg.Topic,
		Value: sarama.ByteEncoder(b),
	}
	_, _, err = k.producer.SendMessage(msg)
	if err != nil {
		k.logger.Printf("failed to send a kafka msg to topic '%s': %v", k.Cfg.Topic, err)
	}
	// 	k.logger.Printf("wrote %d bytes to kafka_topic=%s", len(b), k.Cfg.Topic)
}

// Close //
func (k *KafkaOutput) Close() error {
	return k.producer.Close()
}

// Metrics //
func (k *KafkaOutput) Metrics() []prometheus.Collector { return k.metrics }
<|endoftext|>