<commit_before>/*
 * keyring.go - Handles inserting/removing into user keyrings.
 *
 * Copyright 2017 Google Inc.
 * Author: Joe Richey (joerichey@google.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package security

import (
	"fmt"
	"log"
	"os/user"
	"sync"

	"github.com/pkg/errors"
	"golang.org/x/sys/unix"

	"github.com/google/fscrypt/util"
)

// KeyType is always logon as required by filesystem encryption.
const KeyType = "logon"

// Keyring related error values
var (
	ErrKeySearch         = errors.New("could not find key with descriptor")
	ErrKeyRemove         = util.SystemError("could not remove key from the keyring")
	ErrKeyInsert         = util.SystemError("could not insert key into the keyring")
	ErrSessionUserKeying = errors.New("user keyring not linked into session keyring")
	ErrAccessUserKeyring = errors.New("could not access user keyring")
	ErrLinkUserKeyring   = util.SystemError("could not link user keyring into root keyring")
)

// FindKey tries to locate a key in the kernel keyring with the provided
// description. The key ID is returned if we can find the key. An error is
// returned if the key does not exist.
func FindKey(description string, targetUser *user.User) (int, error) {
	keyringID, err := UserKeyringID(targetUser, false)
	if err != nil {
		return 0, err
	}

	keyID, err := unix.KeyctlSearch(keyringID, KeyType, description, 0)
	log.Printf("KeyctlSearch(%d, %s, %s) = %d, %v", keyringID, KeyType, description, keyID, err)
	if err != nil {
		return 0, errors.Wrap(ErrKeySearch, err.Error())
	}
	return keyID, err
}

// RemoveKey tries to remove a policy key from the kernel keyring with the
// provided description. An error is returned if the key does not exist.
func RemoveKey(description string, targetUser *user.User) error {
	keyID, err := FindKey(description, targetUser)
	if err != nil {
		return err
	}

	// We use KEYCTL_INVALIDATE instead of KEYCTL_REVOKE because
	// invalidating a key immediately removes it.
	_, err = unix.KeyctlInt(unix.KEYCTL_INVALIDATE, keyID, 0, 0, 0)
	log.Printf("KeyctlInvalidate(%d) = %v", keyID, err)
	if err != nil {
		return errors.Wrap(ErrKeyRemove, err.Error())
	}
	return nil
}

// InsertKey puts the provided data into the kernel keyring with the provided
// description.
func InsertKey(data []byte, description string, targetUser *user.User) error {
	keyringID, err := UserKeyringID(targetUser, true)
	if err != nil {
		return err
	}

	keyID, err := unix.AddKey(KeyType, description, data, keyringID)
	log.Printf("KeyctlAddKey(%s, %s, <data>, %d) = %d, %v",
		KeyType, description, keyringID, keyID, err)
	if err != nil {
		return errors.Wrap(ErrKeyInsert, err.Error())
	}
	return nil
}

var (
	keyringIDCache = make(map[int]int)
	cacheLock      sync.Mutex
)

// UserKeyringID returns the key id of the target user's user keyring. We also
// ensure that the keyring will be accessible by linking it into the process
// keyring and linking it into the root user keyring (permissions allowing). If
// checkSession is true, an error is returned if a normal user requests their
// user keyring, but it is not in the current session keyring.
func UserKeyringID(targetUser *user.User, checkSession bool) (int, error) {
	uid := util.AtoiOrPanic(targetUser.Uid)
	targetKeyring, err := userKeyringIDLookup(uid)
	if err != nil {
		return 0, errors.Wrap(ErrAccessUserKeyring, err.Error())
	}

	if !util.IsUserRoot() {
		// Make sure the returned keyring will be accessible by checking
		// that it is in the session keyring.
		if checkSession && !isUserKeyringInSession(uid) {
			return 0, ErrSessionUserKeying
		}
		return targetKeyring, nil
	}

	// Make sure the returned keyring will be accessible by linking it into
	// the root user's user keyring (which will not be garbage collected).
	rootKeyring, err := userKeyringIDLookup(0)
	if err != nil {
		return 0, errors.Wrap(ErrLinkUserKeyring, err.Error())
	}

	if rootKeyring != targetKeyring {
		if err = keyringLink(targetKeyring, rootKeyring); err != nil {
			return 0, errors.Wrap(ErrLinkUserKeyring, err.Error())
		}
	}
	return targetKeyring, nil
}

func userKeyringIDLookup(uid int) (keyringID int, err error) {
	cacheLock.Lock()
	defer cacheLock.Unlock()
	var ok bool
	if keyringID, ok = keyringIDCache[uid]; ok {
		return
	}

	// Our goals here are to:
	//    - Find the user keyring (for the provided uid)
	//    - Link it into the current process keyring (so we can use it)
	//    - Make no permanent changes to the process privileges
	// Complicating this are the facts that:
	//    - The value of KEY_SPEC_USER_KEYRING is determined by the ruid
	//    - Keyring linking permissions use the euid
	// So we have to change both the ruid and euid to make this work,
	// setting the suid to 0 so that we can later switch back.
	ruid, euid, suid := getUids()
	if ruid != uid || euid != uid {
		if err = setUids(uid, uid, 0); err != nil {
			return
		}
		defer func() {
			resetErr := setUids(ruid, euid, suid)
			if resetErr != nil {
				err = resetErr
			}
		}()
	}

	// We get the value of KEY_SPEC_USER_KEYRING. Note that this will also
	// trigger the creation of the uid keyring if it does not yet exist.
	keyringID, err = unix.KeyctlGetKeyringID(unix.KEY_SPEC_USER_KEYRING, true)
	log.Printf("keyringID(_uid.%d) = %d, %v", uid, keyringID, err)
	if err != nil {
		return 0, err
	}

	// We still want to use this keyring after our privileges are reset. So
	// we link it into the process keyring, preventing a loss of access.
	if err = keyringLink(keyringID, unix.KEY_SPEC_PROCESS_KEYRING); err != nil {
		return 0, err
	}

	keyringIDCache[uid] = keyringID
	return keyringID, nil
}

// isUserKeyringInSession tells us if the user's uid keyring is in the current
// session keyring.
func isUserKeyringInSession(uid int) bool {
	// We cannot use unix.KEY_SPEC_SESSION_KEYRING directly as that might
	// create a session keyring if one does not exist.
	sessionKeyring, err := unix.KeyctlGetKeyringID(unix.KEY_SPEC_SESSION_KEYRING, false)
	log.Printf("keyringID(session) = %d, %v", sessionKeyring, err)
	if err != nil {
		return false
	}

	description := fmt.Sprintf("_uid.%d", uid)
	id, err := unix.KeyctlSearch(sessionKeyring, "keyring", description, 0)
	log.Printf("KeyctlSearch(%d, keyring, %s) = %d, %v", sessionKeyring, description, id, err)
	return err == nil
}

func keyringLink(keyID int, keyringID int) error {
	_, err := unix.KeyctlInt(unix.KEYCTL_LINK, keyID, keyringID, 0, 0)
	log.Printf("KeyctlLink(%d, %d) = %v", keyID, keyringID, err)
	return err
}
<commit_msg>keyring: fix permission denied accessing user keyring (#177)<commit_after>/*
 * keyring.go - Handles inserting/removing into user keyrings.
 *
 * Copyright 2017 Google Inc.
 * Author: Joe Richey (joerichey@google.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package security

import (
	"fmt"
	"log"
	"os/user"
	"runtime"

	"github.com/pkg/errors"
	"golang.org/x/sys/unix"

	"github.com/google/fscrypt/util"
)

// KeyType is always logon as required by filesystem encryption.
const KeyType = "logon"

// Keyring related error values
var (
	ErrKeySearch         = errors.New("could not find key with descriptor")
	ErrKeyRemove         = util.SystemError("could not remove key from the keyring")
	ErrKeyInsert         = util.SystemError("could not insert key into the keyring")
	ErrSessionUserKeying = errors.New("user keyring not linked into session keyring")
	ErrAccessUserKeyring = errors.New("could not access user keyring")
	ErrLinkUserKeyring   = util.SystemError("could not link user keyring into root keyring")
)

// FindKey tries to locate a key in the kernel keyring with the provided
// description. The key ID is returned if we can find the key. An error is
// returned if the key does not exist.
func FindKey(description string, targetUser *user.User) (int, error) {
	runtime.LockOSThread() // ensure target user keyring remains possessed in thread keyring
	defer runtime.UnlockOSThread()

	keyringID, err := UserKeyringID(targetUser, false)
	if err != nil {
		return 0, err
	}

	keyID, err := unix.KeyctlSearch(keyringID, KeyType, description, 0)
	log.Printf("KeyctlSearch(%d, %s, %s) = %d, %v", keyringID, KeyType, description, keyID, err)
	if err != nil {
		return 0, errors.Wrap(ErrKeySearch, err.Error())
	}
	return keyID, err
}

// RemoveKey tries to remove a policy key from the kernel keyring with the
// provided description. An error is returned if the key does not exist.
func RemoveKey(description string, targetUser *user.User) error {
	runtime.LockOSThread() // ensure target user keyring remains possessed in thread keyring
	defer runtime.UnlockOSThread()

	keyID, err := FindKey(description, targetUser)
	if err != nil {
		return err
	}

	// We use KEYCTL_INVALIDATE instead of KEYCTL_REVOKE because
	// invalidating a key immediately removes it.
	_, err = unix.KeyctlInt(unix.KEYCTL_INVALIDATE, keyID, 0, 0, 0)
	log.Printf("KeyctlInvalidate(%d) = %v", keyID, err)
	if err != nil {
		return errors.Wrap(ErrKeyRemove, err.Error())
	}
	return nil
}

// InsertKey puts the provided data into the kernel keyring with the provided
// description.
func InsertKey(data []byte, description string, targetUser *user.User) error {
	runtime.LockOSThread() // ensure target user keyring remains possessed in thread keyring
	defer runtime.UnlockOSThread()

	keyringID, err := UserKeyringID(targetUser, true)
	if err != nil {
		return err
	}

	keyID, err := unix.AddKey(KeyType, description, data, keyringID)
	log.Printf("KeyctlAddKey(%s, %s, <data>, %d) = %d, %v",
		KeyType, description, keyringID, keyID, err)
	if err != nil {
		return errors.Wrap(ErrKeyInsert, err.Error())
	}
	return nil
}

// UserKeyringID returns the key id of the target user's user keyring. We also
// ensure that the keyring will be accessible by linking it into the thread
// keyring and linking it into the root user keyring (permissions allowing). If
// checkSession is true, an error is returned if a normal user requests their
// user keyring, but it is not in the current session keyring.
func UserKeyringID(targetUser *user.User, checkSession bool) (int, error) {
	runtime.LockOSThread() // ensure target user keyring remains possessed in thread keyring
	defer runtime.UnlockOSThread()

	uid := util.AtoiOrPanic(targetUser.Uid)
	targetKeyring, err := userKeyringIDLookup(uid)
	if err != nil {
		return 0, errors.Wrap(ErrAccessUserKeyring, err.Error())
	}

	if !util.IsUserRoot() {
		// Make sure the returned keyring will be accessible by checking
		// that it is in the session keyring.
		if checkSession && !isUserKeyringInSession(uid) {
			return 0, ErrSessionUserKeying
		}
		return targetKeyring, nil
	}

	// Make sure the returned keyring will be accessible by linking it into
	// the root user's user keyring (which will not be garbage collected).
	rootKeyring, err := userKeyringIDLookup(0)
	if err != nil {
		return 0, errors.Wrap(ErrLinkUserKeyring, err.Error())
	}

	if rootKeyring != targetKeyring {
		if err = keyringLink(targetKeyring, rootKeyring); err != nil {
			return 0, errors.Wrap(ErrLinkUserKeyring, err.Error())
		}
	}
	return targetKeyring, nil
}

func userKeyringIDLookup(uid int) (keyringID int, err error) {

	// Our goals here are to:
	//    - Find the user keyring (for the provided uid)
	//    - Link it into the current thread keyring (so we can use it)
	//    - Make no permanent changes to the process privileges
	// Complicating this are the facts that:
	//    - The value of KEY_SPEC_USER_KEYRING is determined by the ruid
	//    - Keyring linking permissions use the euid
	// So we have to change both the ruid and euid to make this work,
	// setting the suid to 0 so that we can later switch back.
	ruid, euid, suid := getUids()
	if ruid != uid || euid != uid {
		if err = setUids(uid, uid, 0); err != nil {
			return
		}
		defer func() {
			resetErr := setUids(ruid, euid, suid)
			if resetErr != nil {
				err = resetErr
			}
		}()
	}

	// We get the value of KEY_SPEC_USER_KEYRING. Note that this will also
	// trigger the creation of the uid keyring if it does not yet exist.
	keyringID, err = unix.KeyctlGetKeyringID(unix.KEY_SPEC_USER_KEYRING, true)
	log.Printf("keyringID(_uid.%d) = %d, %v", uid, keyringID, err)
	if err != nil {
		return 0, err
	}

	// We still want to use this keyring after our privileges are reset. So
	// we link it into the thread keyring, preventing a loss of access.
	//
	// We must be under LockOSThread() for this to work reliably.  Note that
	// we can't just use the process keyring, since it doesn't work reliably
	// in Go programs, due to the Go runtime creating threads before the
	// program starts and has a chance to create the process keyring.
	if err = keyringLink(keyringID, unix.KEY_SPEC_THREAD_KEYRING); err != nil {
		return 0, err
	}

	return keyringID, nil
}

// isUserKeyringInSession tells us if the user's uid keyring is in the current
// session keyring.
func isUserKeyringInSession(uid int) bool {
	// We cannot use unix.KEY_SPEC_SESSION_KEYRING directly as that might
	// create a session keyring if one does not exist.
	sessionKeyring, err := unix.KeyctlGetKeyringID(unix.KEY_SPEC_SESSION_KEYRING, false)
	log.Printf("keyringID(session) = %d, %v", sessionKeyring, err)
	if err != nil {
		return false
	}

	description := fmt.Sprintf("_uid.%d", uid)
	id, err := unix.KeyctlSearch(sessionKeyring, "keyring", description, 0)
	log.Printf("KeyctlSearch(%d, keyring, %s) = %d, %v", sessionKeyring, description, id, err)
	return err == nil
}

func keyringLink(keyID int, keyringID int) error {
	_, err := unix.KeyctlInt(unix.KEYCTL_LINK, keyID, keyringID, 0, 0)
	log.Printf("KeyctlLink(%d, %d) = %v", keyID, keyringID, err)
	return err
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Vitess Authors.

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

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

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

package mysql

import (
	"fmt"

	replicationdatapb "vitess.io/vitess/go/vt/proto/replicationdata"
	"vitess.io/vitess/go/vt/vterrors"
)

// SlaveStatus holds replication information from SHOW SLAVE STATUS.
type SlaveStatus struct {
	Position Position
	// RelayLogPosition is the Position that the replica would be at if it
	// were to finish executing everything that's currently in its relay log.
	// However, some MySQL flavors don't expose this information,
	// in which case RelayLogPosition.IsZero() will be true.
	RelayLogPosition     Position
	FilePosition         Position
	FileRelayLogPosition Position
	MasterServerID       uint
	SlaveIORunning       bool
	SlaveSQLRunning      bool
	SecondsBehindMaster  uint
	MasterHost           string
	MasterPort           int
	MasterConnectRetry   int
	MasterUUID           SID
}

// SlaveRunning returns true iff both the Slave IO and Slave SQL threads are
// running.
func (s *SlaveStatus) SlaveRunning() bool {
	return s.SlaveIORunning && s.SlaveSQLRunning
}

// SlaveStatusToProto translates a Status to proto3.
func SlaveStatusToProto(s SlaveStatus) *replicationdatapb.Status {
	return &replicationdatapb.Status{
		Position:             EncodePosition(s.Position),
		RelayLogPosition:     EncodePosition(s.RelayLogPosition),
		FilePosition:         EncodePosition(s.FilePosition),
		FileRelayLogPosition: EncodePosition(s.FileRelayLogPosition),
		MasterServerId:       uint32(s.MasterServerID),
		SlaveIoRunning:       s.SlaveIORunning,
		SlaveSqlRunning:      s.SlaveSQLRunning,
		SecondsBehindMaster:  uint32(s.SecondsBehindMaster),
		MasterHost:           s.MasterHost,
		MasterPort:           int32(s.MasterPort),
		MasterConnectRetry:   int32(s.MasterConnectRetry),
		MasterUuid:           s.MasterUUID.String(),
	}
}

// ProtoToSlaveStatus translates a proto Status, or panics.
func ProtoToSlaveStatus(s *replicationdatapb.Status) SlaveStatus {
	pos, err := DecodePosition(s.Position)
	if err != nil {
		panic(vterrors.Wrapf(err, "cannot decode Position"))
	}
	relayPos, err := DecodePosition(s.RelayLogPosition)
	if err != nil {
		panic(vterrors.Wrapf(err, "cannot decode RelayLogPosition"))
	}
	filePos, err := DecodePosition(s.FilePosition)
	if err != nil {
		panic(vterrors.Wrapf(err, "cannot decode FilePosition"))
	}
	fileRelayPos, err := DecodePosition(s.FileRelayLogPosition)
	if err != nil {
		panic(vterrors.Wrapf(err, "cannot decode FileRelayLogPosition"))
	}
	var sid SID
	if s.MasterUuid != "" {
		sid, err = ParseSID(s.MasterUuid)
		if err != nil {
			panic(vterrors.Wrapf(err, "cannot decode MasterUUID"))
		}
	}
	return SlaveStatus{
		Position:             pos,
		RelayLogPosition:     relayPos,
		FilePosition:         filePos,
		FileRelayLogPosition: fileRelayPos,
		MasterServerID:       uint(s.MasterServerId),
		SlaveIORunning:       s.SlaveIoRunning,
		SlaveSQLRunning:      s.SlaveSqlRunning,
		SecondsBehindMaster:  uint(s.SecondsBehindMaster),
		MasterHost:           s.MasterHost,
		MasterPort:           int(s.MasterPort),
		MasterConnectRetry:   int(s.MasterConnectRetry),
		MasterUUID:           sid,
	}
}

// FindErrantGTIDs can be used to find ErrantGTIDs in the receiver's relay log, by comparing it against all known replicas,
// provided as a list of SlaveStatus's. This method only works if the flavor for all retrieved SlaveStatus's is MySQL.
// The result is returned at a Mysql56GTIDSet, where everything in the GTIDSet is a found errant GTID.
func (s *SlaveStatus) FindErrantGTIDs(otherReplicaStatuses []*SlaveStatus) (Mysql56GTIDSet, error) {
	set, ok := s.RelayLogPosition.GTIDSet.(Mysql56GTIDSet)
	if !ok {
		return nil, fmt.Errorf("errant GTIDs can only be computed on the MySQL flavor")
	}

	otherSets := make([]Mysql56GTIDSet, 0, len(otherReplicaStatuses))
	for _, status := range otherReplicaStatuses {
		otherSet, ok := status.RelayLogPosition.GTIDSet.(Mysql56GTIDSet)
		if !ok {
			panic("The receiver SlaveStatus contained a Mysql56GTIDSet in its relay log, but a replica's SlaveStatus is of another flavor. This should never happen.")
		}
		// Throw out master SID from consideration.
		for sid := range otherSet {
			if sid == status.MasterUUID {
				delete(otherSet, sid)
			}
		}

		otherSets = append(otherSets, otherSet)
	}

	// Copy set for final diffSet so we don't mutate receiver.
	diffSet := make(Mysql56GTIDSet, len(set))
	for sid, intervals := range set {
		if sid == s.MasterUUID {
			continue
		}
		diffSet[sid] = intervals
	}

	for _, otherSet := range otherSets {
		diffSet = diffSet.Difference(otherSet)
	}

	if len(diffSet) == 0 {
		// If diffSet is empty, then we have no errant GTIDs.
		return nil, nil
	}

	return diffSet, nil
}
<commit_msg>Improve comment per review suggestions.<commit_after>/*
Copyright 2019 The Vitess Authors.

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

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

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

package mysql

import (
	"fmt"

	replicationdatapb "vitess.io/vitess/go/vt/proto/replicationdata"
	"vitess.io/vitess/go/vt/vterrors"
)

// SlaveStatus holds replication information from SHOW SLAVE STATUS.
type SlaveStatus struct {
	Position Position
	// RelayLogPosition is the Position that the replica would be at if it
	// were to finish executing everything that's currently in its relay log.
	// However, some MySQL flavors don't expose this information,
	// in which case RelayLogPosition.IsZero() will be true.
	RelayLogPosition     Position
	FilePosition         Position
	FileRelayLogPosition Position
	MasterServerID       uint
	SlaveIORunning       bool
	SlaveSQLRunning      bool
	SecondsBehindMaster  uint
	MasterHost           string
	MasterPort           int
	MasterConnectRetry   int
	MasterUUID           SID
}

// SlaveRunning returns true iff both the Slave IO and Slave SQL threads are
// running.
func (s *SlaveStatus) SlaveRunning() bool {
	return s.SlaveIORunning && s.SlaveSQLRunning
}

// SlaveStatusToProto translates a Status to proto3.
func SlaveStatusToProto(s SlaveStatus) *replicationdatapb.Status {
	return &replicationdatapb.Status{
		Position:             EncodePosition(s.Position),
		RelayLogPosition:     EncodePosition(s.RelayLogPosition),
		FilePosition:         EncodePosition(s.FilePosition),
		FileRelayLogPosition: EncodePosition(s.FileRelayLogPosition),
		MasterServerId:       uint32(s.MasterServerID),
		SlaveIoRunning:       s.SlaveIORunning,
		SlaveSqlRunning:      s.SlaveSQLRunning,
		SecondsBehindMaster:  uint32(s.SecondsBehindMaster),
		MasterHost:           s.MasterHost,
		MasterPort:           int32(s.MasterPort),
		MasterConnectRetry:   int32(s.MasterConnectRetry),
		MasterUuid:           s.MasterUUID.String(),
	}
}

// ProtoToSlaveStatus translates a proto Status, or panics.
func ProtoToSlaveStatus(s *replicationdatapb.Status) SlaveStatus {
	pos, err := DecodePosition(s.Position)
	if err != nil {
		panic(vterrors.Wrapf(err, "cannot decode Position"))
	}
	relayPos, err := DecodePosition(s.RelayLogPosition)
	if err != nil {
		panic(vterrors.Wrapf(err, "cannot decode RelayLogPosition"))
	}
	filePos, err := DecodePosition(s.FilePosition)
	if err != nil {
		panic(vterrors.Wrapf(err, "cannot decode FilePosition"))
	}
	fileRelayPos, err := DecodePosition(s.FileRelayLogPosition)
	if err != nil {
		panic(vterrors.Wrapf(err, "cannot decode FileRelayLogPosition"))
	}
	var sid SID
	if s.MasterUuid != "" {
		sid, err = ParseSID(s.MasterUuid)
		if err != nil {
			panic(vterrors.Wrapf(err, "cannot decode MasterUUID"))
		}
	}
	return SlaveStatus{
		Position:             pos,
		RelayLogPosition:     relayPos,
		FilePosition:         filePos,
		FileRelayLogPosition: fileRelayPos,
		MasterServerID:       uint(s.MasterServerId),
		SlaveIORunning:       s.SlaveIoRunning,
		SlaveSQLRunning:      s.SlaveSqlRunning,
		SecondsBehindMaster:  uint(s.SecondsBehindMaster),
		MasterHost:           s.MasterHost,
		MasterPort:           int(s.MasterPort),
		MasterConnectRetry:   int(s.MasterConnectRetry),
		MasterUUID:           sid,
	}
}

// FindErrantGTIDs can be used to find errant GTIDs in the receiver's relay log, by comparing it against all known replicas,
// provided as a list of SlaveStatus's. This method only works if the flavor for all retrieved SlaveStatus's is MySQL.
// The result is returned as a Mysql56GTIDSet, each of whose elements is a found errant GTID.
func (s *SlaveStatus) FindErrantGTIDs(otherReplicaStatuses []*SlaveStatus) (Mysql56GTIDSet, error) {
	set, ok := s.RelayLogPosition.GTIDSet.(Mysql56GTIDSet)
	if !ok {
		return nil, fmt.Errorf("errant GTIDs can only be computed on the MySQL flavor")
	}

	otherSets := make([]Mysql56GTIDSet, 0, len(otherReplicaStatuses))
	for _, status := range otherReplicaStatuses {
		otherSet, ok := status.RelayLogPosition.GTIDSet.(Mysql56GTIDSet)
		if !ok {
			panic("The receiver SlaveStatus contained a Mysql56GTIDSet in its relay log, but a replica's SlaveStatus is of another flavor. This should never happen.")
		}
		// Throw out master SID from consideration.
		for sid := range otherSet {
			if sid == status.MasterUUID {
				delete(otherSet, sid)
			}
		}

		otherSets = append(otherSets, otherSet)
	}

	// Copy set for final diffSet so we don't mutate receiver.
	diffSet := make(Mysql56GTIDSet, len(set))
	for sid, intervals := range set {
		if sid == s.MasterUUID {
			continue
		}
		diffSet[sid] = intervals
	}

	for _, otherSet := range otherSets {
		diffSet = diffSet.Difference(otherSet)
	}

	if len(diffSet) == 0 {
		// If diffSet is empty, then we have no errant GTIDs.
		return nil, nil
	}

	return diffSet, nil
}
<|endoftext|>


<commit_before>package shipit

func Add(a, b int) int {
	return a + b
}
<commit_msg>break the build<commit_after>package shipit

func Add(a, b int) int {
	return a - b
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"os"
	"path"
	"runtime"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"gopkg.in/vbauerster/mpb.v2"
)

type patchFile struct {
	path    string
	version string
	blocks  []patchBlock
}

func writePatchFile(patch patchFile) error {
	log.Debugf("Writing %s", patch.path)

	err := os.Remove(patch.path)
	check(err)

	f, err := os.Create(patch.path)
	check(err)
	defer f.Close()

	w := bufio.NewWriter(f)

	_, err = w.WriteString(fmt.Sprintf("> %s\n", patch.version))
	check(err)

	for _, block := range patch.blocks {
		_, err = w.WriteString("> BEGIN STRING\n")
		check(err)

		_, err = w.WriteString(block.original)
		check(err)

		for _, context := range block.contexts {
			context = fmt.Sprintf("> CONTEXT: %s", context)

			if !block.translated {
				context += " < UNTRANSLATED\n"
			} else {
				context += "\n"
			}
			_, err = w.WriteString(context)
			check(err)
		}

		var trans string

		if block.translated {
			trans = breakLines(block.translation)
		} else {
			trans = "\n"
		}
		_, err = w.WriteString(trans)
		check(err)

		_, err = w.WriteString("> END STRING\n\n")
		check(err)
	}

	err = w.Flush()
	check(err)

	log.Debugf("Done writing %s", patch.path)

	return nil
}

func parsePatchFile(file string) (patchFile, error) {
	log.Debugf("Parsing %q", path.Base(file))

	f, err := os.Open(file)
	defer f.Close()

	patch := patchFile{path: file}

	s := bufio.NewScanner(f)
	s.Split(bufio.ScanLines)

	original := false
	translation := false

	var orig string
	var trans string
	var contexts []string

	var block patchBlock

	for s.Scan() {
		l := s.Text()

		if strings.HasPrefix(l, "> ") {
			l = l[2:]

			switch {
			case strings.HasPrefix(l, "RPGMAKER TRANS PATCH FILE VERSION"):
				patch.version = l
			case strings.HasPrefix(l, "BEGIN STRING"):
				original = true
			case strings.HasPrefix(l, "CONTEXT: "):
				if len(l) > len("CONTEXT: ")+1 {
					start := len("CONTEXT: ")
					end := strings.Index(l, " < UNTRANSLATED")
					if end == -1 {
						contexts = append(contexts, l[start:])
					} else {
						contexts = append(contexts, l[start:end])
					}
				} else {
					log.Warn("Empty context?", l)
				}

				original = false
				translation = true
			case strings.HasPrefix(l, "END STRING"):
				translation = false

				block.original = orig
				block.contexts = contexts

				if len(strings.TrimRight(trans, "\n")) < 1 {
					block.translation = ""
				} else {
					block.translated = true
					block.translation = trans
				}

				//log.Info(spew.Sdump(block))

				patch.blocks = append(patch.blocks, block)

				orig = ""
				trans = ""
				contexts = nil
			default:
				log.Warn("Unknown input:", l)
			}

			continue
		}

		if !strings.HasSuffix(l, "\n") && (original || translation) {
			l += "\n"
		}

		if original {
			orig += l
		} else if translation {
			trans += l
		}
	}

	return patch, err
}

func translatePatch(patch patchFile) (patchFile, error) {
	var err error

	if strings.HasSuffix(patch.path, "Scripts.txt") {
		fmt.Println("Skipped")

		return patch, err
	}

	// Only needed to preserve order in patch file
	type blockWork struct {
		id    int
		block patchBlock
	}

	jobs := make(chan blockWork, runtime.NumCPU()*2)
	results := make(chan blockWork, runtime.NumCPU()*2)

	// Start workers
	for w := 1; w <= runtime.NumCPU(); w++ {
		go func(jobs <-chan blockWork, results chan<- blockWork) {
			for j := range jobs {
				j.block = parseBlock(j.block)
				results <- j
			}
		}(jobs, results)
	}

	p := mpb.New().
		RefreshRate(500 * time.Millisecond)

	defer p.Stop()

	count := len(patch.blocks)

	bar := p.AddBar(int64(count)).
		PrependCounters("%3s/%3s", 0, 10, mpb.DwidthSync|mpb.DextraSpace).
		AppendETA(2, 0)

	// Add blocks in background to job queue
	go func() {
		for i, block := range patch.blocks {
			//patch.blocks[i] = parseBlock(block)
			w := blockWork{i, block}
			jobs <- w
		}
		close(jobs)
	}()

	// Start reading results, will block if there are none
	for a := count; a > 0; a-- {

		j := <-results

		patch.blocks[j.id] = j.block

		bar.Incr(1)
	}

	return patch, err
}
<commit_msg>Align progress bar block counter to 4 digits<commit_after>package main

import (
	"bufio"
	"fmt"
	"os"
	"path"
	"runtime"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"gopkg.in/vbauerster/mpb.v2"
)

type patchFile struct {
	path    string
	version string
	blocks  []patchBlock
}

func writePatchFile(patch patchFile) error {
	log.Debugf("Writing %s", patch.path)

	err := os.Remove(patch.path)
	check(err)

	f, err := os.Create(patch.path)
	check(err)
	defer f.Close()

	w := bufio.NewWriter(f)

	_, err = w.WriteString(fmt.Sprintf("> %s\n", patch.version))
	check(err)

	for _, block := range patch.blocks {
		_, err = w.WriteString("> BEGIN STRING\n")
		check(err)

		_, err = w.WriteString(block.original)
		check(err)

		for _, context := range block.contexts {
			context = fmt.Sprintf("> CONTEXT: %s", context)

			if !block.translated {
				context += " < UNTRANSLATED\n"
			} else {
				context += "\n"
			}
			_, err = w.WriteString(context)
			check(err)
		}

		var trans string

		if block.translated {
			trans = breakLines(block.translation)
		} else {
			trans = "\n"
		}
		_, err = w.WriteString(trans)
		check(err)

		_, err = w.WriteString("> END STRING\n\n")
		check(err)
	}

	err = w.Flush()
	check(err)

	log.Debugf("Done writing %s", patch.path)

	return nil
}

func parsePatchFile(file string) (patchFile, error) {
	log.Debugf("Parsing %q", path.Base(file))

	f, err := os.Open(file)
	defer f.Close()

	patch := patchFile{path: file}

	s := bufio.NewScanner(f)
	s.Split(bufio.ScanLines)

	original := false
	translation := false

	var orig string
	var trans string
	var contexts []string

	var block patchBlock

	for s.Scan() {
		l := s.Text()

		if strings.HasPrefix(l, "> ") {
			l = l[2:]

			switch {
			case strings.HasPrefix(l, "RPGMAKER TRANS PATCH FILE VERSION"):
				patch.version = l
			case strings.HasPrefix(l, "BEGIN STRING"):
				original = true
			case strings.HasPrefix(l, "CONTEXT: "):
				if len(l) > len("CONTEXT: ")+1 {
					start := len("CONTEXT: ")
					end := strings.Index(l, " < UNTRANSLATED")
					if end == -1 {
						contexts = append(contexts, l[start:])
					} else {
						contexts = append(contexts, l[start:end])
					}
				} else {
					log.Warn("Empty context?", l)
				}

				original = false
				translation = true
			case strings.HasPrefix(l, "END STRING"):
				translation = false

				block.original = orig
				block.contexts = contexts

				if len(strings.TrimRight(trans, "\n")) < 1 {
					block.translation = ""
				} else {
					block.translated = true
					block.translation = trans
				}

				//log.Info(spew.Sdump(block))

				patch.blocks = append(patch.blocks, block)

				orig = ""
				trans = ""
				contexts = nil
			default:
				log.Warn("Unknown input:", l)
			}

			continue
		}

		if !strings.HasSuffix(l, "\n") && (original || translation) {
			l += "\n"
		}

		if original {
			orig += l
		} else if translation {
			trans += l
		}
	}

	return patch, err
}

func translatePatch(patch patchFile) (patchFile, error) {
	var err error

	if strings.HasSuffix(patch.path, "Scripts.txt") {
		fmt.Println("Skipped")

		return patch, err
	}

	// Only needed to preserve order in patch file
	type blockWork struct {
		id    int
		block patchBlock
	}

	jobs := make(chan blockWork, runtime.NumCPU()*2)
	results := make(chan blockWork, runtime.NumCPU()*2)

	// Start workers
	for w := 1; w <= runtime.NumCPU(); w++ {
		go func(jobs <-chan blockWork, results chan<- blockWork) {
			for j := range jobs {
				j.block = parseBlock(j.block)
				results <- j
			}
		}(jobs, results)
	}

	p := mpb.New().
		RefreshRate(500 * time.Millisecond)

	defer p.Stop()

	count := len(patch.blocks)

	bar := p.AddBar(int64(count)).
		PrependCounters("%4s/%4s", 0, 10, mpb.DwidthSync|mpb.DextraSpace).
		AppendETA(2, 0)

	// Add blocks in background to job queue
	go func() {
		for i, block := range patch.blocks {
			//patch.blocks[i] = parseBlock(block)
			w := blockWork{i, block}
			jobs <- w
		}
		close(jobs)
	}()

	// Start reading results, will block if there are none
	for a := count; a > 0; a-- {

		j := <-results

		patch.blocks[j.id] = j.block

		bar.Incr(1)
	}

	return patch, err
}
<|endoftext|>


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

package main

import (
	"context"
	"fmt"
	"io"
	"net"
	"net/http"
	_ "net/http/pprof"
	"os"
	"path/filepath"

	"github.com/spf13/cobra"
	"github.com/syndtr/gocapability/capability"
	"github.com/vishvananda/netlink"

	libconfig "github.com/scionproto/scion/go/lib/config"
	"github.com/scionproto/scion/go/lib/env"
	"github.com/scionproto/scion/go/lib/fatal"
	"github.com/scionproto/scion/go/lib/log"
	"github.com/scionproto/scion/go/lib/prom"
	"github.com/scionproto/scion/go/lib/sciond"
	"github.com/scionproto/scion/go/lib/serrors"
	"github.com/scionproto/scion/go/lib/snet/addrutil"
	"github.com/scionproto/scion/go/lib/sock/reliable"
	"github.com/scionproto/scion/go/pkg/command"
	"github.com/scionproto/scion/go/pkg/gateway"
	"github.com/scionproto/scion/go/pkg/gateway/xnet"
	"github.com/scionproto/scion/go/pkg/service"
	"github.com/scionproto/scion/go/posix-gateway/config"
)

func main() {
	var flags struct {
		config string
	}
	executable := filepath.Base(os.Args[0])
	cmd := &cobra.Command{
		Use:           executable,
		Short:         "SCION IP gateway",
		Example:       "  " + executable + " --config gateway.toml",
		SilenceErrors: true,
		SilenceUsage:  true,
		Args:          cobra.NoArgs,
		RunE: func(cmd *cobra.Command, args []string) error {
			return run(flags.config)
		},
	}
	cmd.AddCommand(
		command.NewCompletion(cmd),
		command.NewSample(cmd,
			command.NewSampleConfig(&config.Config{}),
		),
		command.NewVersion(cmd),
	)
	cmd.Flags().StringVar(&flags.config, "config", "", "Configuration file (required)")
	cmd.MarkFlagRequired("config")
	if err := cmd.Execute(); err != nil {
		fmt.Fprintf(os.Stderr, "Error: %s\n", err)
		os.Exit(1)
	}
}

func run(file string) error {
	fatal.Init()
	cfg, err := setupBasic(file)
	if err != nil {
		return err
	}
	defer log.Flush()
	defer env.LogAppStopped("Gateway", cfg.Gateway.ID)
	defer log.HandlePanic()
	if err := cfg.Validate(); err != nil {
		return serrors.WrapStr("validating config", err)
	}
	cfg.Metrics.StartPrometheus()

	reloadConfigTrigger := make(chan struct{})

	tunnelLink, tunnelIO, err := initTunnel(cfg)
	if err != nil {
		return serrors.WrapStr("initializing TUN device", err)
	}
	log.Debug("Tunnel device initialized", "dev", cfg.Tunnel.Name)

	if !gateway.ExperimentalExportMainRT() {
		if err := dropNetworkCapabilities(); err != nil {
			return serrors.WrapStr("dropping capabilities", err)
		}
		log.Debug("Network capabilities dropped (dropped CAP_NET_ADMIN)")
	}

	daemonService := &sciond.Service{
		Address: cfg.Daemon.Address,
	}
	daemon, err := daemonService.Connect(context.TODO())
	if err != nil {
		return serrors.WrapStr("connecting to daemon", err)
	}

	controlAddress, err := net.ResolveUDPAddr("udp", cfg.Gateway.CtrlAddr)
	if err != nil {
		return serrors.WrapStr("parsing control address", err)
	}
	if len(controlAddress.IP) == 0 {
		controlAddress.IP, err = addrutil.DefaultLocalIP(context.Background(), daemon)
		if err != nil {
			return serrors.WrapStr("determine default local IP", err)
		}
	}
	dataAddress, err := net.ResolveUDPAddr("udp", cfg.Gateway.DataAddr)
	if err != nil {
		return serrors.WrapStr("parsing data address", err)
	}
	if len(dataAddress.IP) == 0 {
		dataAddress.IP = controlAddress.IP
		dataAddress.Zone = controlAddress.Zone
	}
	httpPages := service.StatusPages{
		"info":      service.NewInfoHandler(),
		"config":    service.NewConfigHandler(cfg),
		"log/level": log.ConsoleLevel.ServeHTTP,
	}
	gw := &gateway.Gateway{
		TrafficPolicyFile:        cfg.Gateway.TrafficPolicy,
		RoutingPolicyFile:        cfg.Gateway.IPRoutingPolicy,
		ControlServerAddr:        controlAddress,
		ControlClientIP:          controlAddress.IP,
		ServiceDiscoveryClientIP: controlAddress.IP,
		PathMonitorIP:            controlAddress.IP,
		ProbeServerAddr:          &net.UDPAddr{IP: controlAddress.IP, Port: 30856},
		ProbeClientIP:            controlAddress.IP,
		DataServerAddr:           dataAddress,
		DataClientIP:             dataAddress.IP,
		Dispatcher:               reliable.NewDispatcher(""),
		Daemon:                   daemon,
		InternalDevice:           tunnelIO,
		RouteDevice:              tunnelLink,
		RouteSource:              controlAddress.IP,
		ConfigReloadTrigger:      reloadConfigTrigger,
		HTTPEndpoints:            httpPages,
		HTTPServeMux:             http.DefaultServeMux,
		Logger:                   log.New(),
		Metrics:                  gateway.NewMetrics(),
	}

	errs := make(chan error, 1)
	go func() {
		defer log.HandlePanic()
		if err := gw.Run(); err != nil {
			errs <- err
		}
	}()

	env.SetupEnv(func() {
		reloadConfigTrigger <- struct{}{}
	})

	select {
	case err := <-errs:
		return err
	case <-fatal.ShutdownChan():
		return nil
	case <-fatal.FatalChan():
		return serrors.New("received fatal error")
	}
}

// setupBasic loads the config from file and initializes logging.
func setupBasic(file string) (config.Config, error) {
	var cfg config.Config
	if err := libconfig.LoadFile(file, &cfg); err != nil {
		return config.Config{}, serrors.WrapStr("loading config from file", err, "file", file)
	}
	cfg.InitDefaults()
	if err := log.Setup(cfg.Logging); err != nil {
		return config.Config{}, serrors.WrapStr("initialize logging", err)
	}
	prom.ExportElementID(cfg.Gateway.ID)
	if err := env.LogAppStarted("Gateway", cfg.Gateway.ID); err != nil {
		return config.Config{}, err
	}
	return cfg, nil
}

func initTunnel(cfg config.Config) (netlink.Link, io.ReadWriteCloser, error) {
	tunLink, tunIO, err := xnet.ConnectTun(cfg.Tunnel.Name)
	if err != nil {
		return nil, nil, err
	}

	if !gateway.ExperimentalExportMainRT() {
		defaultV4Net := &net.IPNet{IP: net.IPv4zero, Mask: net.CIDRMask(0, net.IPv4len*8)}
		err = xnet.AddRoute(cfg.Tunnel.RoutingTableID, tunLink, defaultV4Net, nil)
		if err != nil {
			return nil, nil,
				serrors.WrapStr("adding default IPv4 route to gateway routing table", err)
		}

		// FIXME(scrye): Uncomment this when we start testing IPv6.
		// defaultV6Net := &net.IPNet{IP: net.IPv6zero, Mask: net.CIDRMask(0, net.IPv6len*8)}
		// err = xnet.AddRoute(cfg.Tunnel.RoutingTableID, tunLink, DefaultV6Net, nil)
		// if err != nil {
		// 	return nil, nil,
		// serrors.WrapStr("adding default IPv6 route to gateway routing table", err)
		// }
	}
	return tunLink, tunIO, nil
}

func dropNetworkCapabilities() error {
	caps, err := capability.NewPid(0)
	if err != nil {
		return serrors.WrapStr("retrieving capabilities", err)
	}
	caps.Clear(capability.CAPS)
	caps.Apply(capability.CAPS)
	return nil
}
<commit_msg>gateway: use data address<commit_after>// Copyright 2020 Anapaya Systems
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"context"
	"fmt"
	"io"
	"net"
	"net/http"
	_ "net/http/pprof"
	"os"
	"path/filepath"

	"github.com/spf13/cobra"
	"github.com/syndtr/gocapability/capability"
	"github.com/vishvananda/netlink"

	libconfig "github.com/scionproto/scion/go/lib/config"
	"github.com/scionproto/scion/go/lib/env"
	"github.com/scionproto/scion/go/lib/fatal"
	"github.com/scionproto/scion/go/lib/log"
	"github.com/scionproto/scion/go/lib/prom"
	"github.com/scionproto/scion/go/lib/sciond"
	"github.com/scionproto/scion/go/lib/serrors"
	"github.com/scionproto/scion/go/lib/snet/addrutil"
	"github.com/scionproto/scion/go/lib/sock/reliable"
	"github.com/scionproto/scion/go/pkg/command"
	"github.com/scionproto/scion/go/pkg/gateway"
	"github.com/scionproto/scion/go/pkg/gateway/xnet"
	"github.com/scionproto/scion/go/pkg/service"
	"github.com/scionproto/scion/go/posix-gateway/config"
)

func main() {
	var flags struct {
		config string
	}
	executable := filepath.Base(os.Args[0])
	cmd := &cobra.Command{
		Use:           executable,
		Short:         "SCION IP gateway",
		Example:       "  " + executable + " --config gateway.toml",
		SilenceErrors: true,
		SilenceUsage:  true,
		Args:          cobra.NoArgs,
		RunE: func(cmd *cobra.Command, args []string) error {
			return run(flags.config)
		},
	}
	cmd.AddCommand(
		command.NewCompletion(cmd),
		command.NewSample(cmd,
			command.NewSampleConfig(&config.Config{}),
		),
		command.NewVersion(cmd),
	)
	cmd.Flags().StringVar(&flags.config, "config", "", "Configuration file (required)")
	cmd.MarkFlagRequired("config")
	if err := cmd.Execute(); err != nil {
		fmt.Fprintf(os.Stderr, "Error: %s\n", err)
		os.Exit(1)
	}
}

func run(file string) error {
	fatal.Init()
	cfg, err := setupBasic(file)
	if err != nil {
		return err
	}
	defer log.Flush()
	defer env.LogAppStopped("Gateway", cfg.Gateway.ID)
	defer log.HandlePanic()
	if err := cfg.Validate(); err != nil {
		return serrors.WrapStr("validating config", err)
	}
	cfg.Metrics.StartPrometheus()

	reloadConfigTrigger := make(chan struct{})

	tunnelLink, tunnelIO, err := initTunnel(cfg)
	if err != nil {
		return serrors.WrapStr("initializing TUN device", err)
	}
	log.Debug("Tunnel device initialized", "dev", cfg.Tunnel.Name)

	if !gateway.ExperimentalExportMainRT() {
		if err := dropNetworkCapabilities(); err != nil {
			return serrors.WrapStr("dropping capabilities", err)
		}
		log.Debug("Network capabilities dropped (dropped CAP_NET_ADMIN)")
	}

	daemonService := &sciond.Service{
		Address: cfg.Daemon.Address,
	}
	daemon, err := daemonService.Connect(context.TODO())
	if err != nil {
		return serrors.WrapStr("connecting to daemon", err)
	}

	controlAddress, err := net.ResolveUDPAddr("udp", cfg.Gateway.CtrlAddr)
	if err != nil {
		return serrors.WrapStr("parsing control address", err)
	}
	if len(controlAddress.IP) == 0 {
		controlAddress.IP, err = addrutil.DefaultLocalIP(context.Background(), daemon)
		if err != nil {
			return serrors.WrapStr("determine default local IP", err)
		}
	}
	dataAddress, err := net.ResolveUDPAddr("udp", cfg.Gateway.DataAddr)
	if err != nil {
		return serrors.WrapStr("parsing data address", err)
	}
	if len(dataAddress.IP) == 0 {
		dataAddress.IP = controlAddress.IP
		dataAddress.Zone = controlAddress.Zone
	}
	httpPages := service.StatusPages{
		"info":      service.NewInfoHandler(),
		"config":    service.NewConfigHandler(cfg),
		"log/level": log.ConsoleLevel.ServeHTTP,
	}
	gw := &gateway.Gateway{
		TrafficPolicyFile:        cfg.Gateway.TrafficPolicy,
		RoutingPolicyFile:        cfg.Gateway.IPRoutingPolicy,
		ControlServerAddr:        controlAddress,
		ControlClientIP:          controlAddress.IP,
		ServiceDiscoveryClientIP: controlAddress.IP,
		PathMonitorIP:            controlAddress.IP,
		ProbeServerAddr:          &net.UDPAddr{IP: controlAddress.IP, Port: 30856},
		ProbeClientIP:            controlAddress.IP,
		DataServerAddr:           dataAddress,
		DataClientIP:             dataAddress.IP,
		Dispatcher:               reliable.NewDispatcher(""),
		Daemon:                   daemon,
		InternalDevice:           tunnelIO,
		RouteDevice:              tunnelLink,
		RouteSource:              dataAddress.IP,
		ConfigReloadTrigger:      reloadConfigTrigger,
		HTTPEndpoints:            httpPages,
		HTTPServeMux:             http.DefaultServeMux,
		Logger:                   log.New(),
		Metrics:                  gateway.NewMetrics(),
	}

	errs := make(chan error, 1)
	go func() {
		defer log.HandlePanic()
		if err := gw.Run(); err != nil {
			errs <- err
		}
	}()

	env.SetupEnv(func() {
		reloadConfigTrigger <- struct{}{}
	})

	select {
	case err := <-errs:
		return err
	case <-fatal.ShutdownChan():
		return nil
	case <-fatal.FatalChan():
		return serrors.New("received fatal error")
	}
}

// setupBasic loads the config from file and initializes logging.
func setupBasic(file string) (config.Config, error) {
	var cfg config.Config
	if err := libconfig.LoadFile(file, &cfg); err != nil {
		return config.Config{}, serrors.WrapStr("loading config from file", err, "file", file)
	}
	cfg.InitDefaults()
	if err := log.Setup(cfg.Logging); err != nil {
		return config.Config{}, serrors.WrapStr("initialize logging", err)
	}
	prom.ExportElementID(cfg.Gateway.ID)
	if err := env.LogAppStarted("Gateway", cfg.Gateway.ID); err != nil {
		return config.Config{}, err
	}
	return cfg, nil
}

func initTunnel(cfg config.Config) (netlink.Link, io.ReadWriteCloser, error) {
	tunLink, tunIO, err := xnet.ConnectTun(cfg.Tunnel.Name)
	if err != nil {
		return nil, nil, err
	}

	if !gateway.ExperimentalExportMainRT() {
		defaultV4Net := &net.IPNet{IP: net.IPv4zero, Mask: net.CIDRMask(0, net.IPv4len*8)}
		err = xnet.AddRoute(cfg.Tunnel.RoutingTableID, tunLink, defaultV4Net, nil)
		if err != nil {
			return nil, nil,
				serrors.WrapStr("adding default IPv4 route to gateway routing table", err)
		}

		// FIXME(scrye): Uncomment this when we start testing IPv6.
		// defaultV6Net := &net.IPNet{IP: net.IPv6zero, Mask: net.CIDRMask(0, net.IPv6len*8)}
		// err = xnet.AddRoute(cfg.Tunnel.RoutingTableID, tunLink, DefaultV6Net, nil)
		// if err != nil {
		// 	return nil, nil,
		// serrors.WrapStr("adding default IPv6 route to gateway routing table", err)
		// }
	}
	return tunLink, tunIO, nil
}

func dropNetworkCapabilities() error {
	caps, err := capability.NewPid(0)
	if err != nil {
		return serrors.WrapStr("retrieving capabilities", err)
	}
	caps.Clear(capability.CAPS)
	caps.Apply(capability.CAPS)
	return nil
}
<|endoftext|>


<commit_before>package jwthelper

import (
	"fmt"

	"github.com/dgrijalva/jwt-go"
)

// Signer is used to sign JWT tokens.
// It stores signing method and key internally.
type Signer struct {
	method jwt.SigningMethod
	key    interface{}
}

// SignerOption represents the option for JWT token signing.
type SignerOption struct {
	f func(s *Signer)
}

var (
	ErrInvalidSigner = fmt.Errorf("invalid signer")
)

func SignerMethod(m jwt.SigningMethod) SignerOption {
	return SignerOption{func(s *Signer) {
		s.method = m
	}}
}

func NewRSASHASigner(key []byte, options ...SignerOption) *Signer {
	s := &Signer{
		// Default signing method: RSASHA-256.
		method: jwt.SigningMethodRS256,
	}

	// Override customized options.
	for _, op := range options {
		op.f(s)
	}

	privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(key)
	if err != nil {
		return &Signer{}
	}

	s.key = privateKey
	return s
}

func NewRSASHASignerFromPEMFile(privatePEM string, options ...SignerOption) *Signer {
	key, err := ReadKey(privatePEM)
	if err != nil {
		return &Signer{}
	}

	return NewRSASHASigner(key, options...)
}

func (s *Signer) Valid() bool {
	if s.method == nil || s.key == nil {
		return false
	}
	return true
}

func (s *Signer) SignedString(claims ...Claim) (string, error) {
	if !s.Valid() {
		return "", ErrInvalidSigner
	}

	myClaims := newClaims()

	for _, claim := range claims {
		claim.f(&myClaims)
	}

	token := jwt.NewWithClaims(s.method, myClaims.claims)
	return token.SignedString(s.key)
}
<commit_msg>Add comment of ErrInvalidSigner<commit_after>package jwthelper

import (
	"fmt"

	"github.com/dgrijalva/jwt-go"
)

// Signer is used to sign JWT tokens.
// It stores signing method and key internally.
type Signer struct {
	method jwt.SigningMethod
	key    interface{}
}

// SignerOption represents the option for JWT token signing.
type SignerOption struct {
	f func(s *Signer)
}

var (
	// ErrInvalidSigner is the error of invalid signer.
	ErrInvalidSigner = fmt.Errorf("invalid signer")
)

func SignerMethod(m jwt.SigningMethod) SignerOption {
	return SignerOption{func(s *Signer) {
		s.method = m
	}}
}

func NewRSASHASigner(key []byte, options ...SignerOption) *Signer {
	s := &Signer{
		// Default signing method: RSASHA-256.
		method: jwt.SigningMethodRS256,
	}

	// Override customized options.
	for _, op := range options {
		op.f(s)
	}

	privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(key)
	if err != nil {
		return &Signer{}
	}

	s.key = privateKey
	return s
}

func NewRSASHASignerFromPEMFile(privatePEM string, options ...SignerOption) *Signer {
	key, err := ReadKey(privatePEM)
	if err != nil {
		return &Signer{}
	}

	return NewRSASHASigner(key, options...)
}

func (s *Signer) Valid() bool {
	if s.method == nil || s.key == nil {
		return false
	}
	return true
}

func (s *Signer) SignedString(claims ...Claim) (string, error) {
	if !s.Valid() {
		return "", ErrInvalidSigner
	}

	myClaims := newClaims()

	for _, claim := range claims {
		claim.f(&myClaims)
	}

	token := jwt.NewWithClaims(s.method, myClaims.claims)
	return token.SignedString(s.key)
}
<|endoftext|>


<commit_before>/*
 Copyright (c) 2014 Soichiro Kashima
*/
package main

import (
	"bufio"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"regexp"
)

const BufSize = 4096

type Opt struct {
	SrcRoot    string
	ProjName   string
	ConfigPath string
	Locale     string
}

type Rule struct {
	Pattern string
	Message map[string]string
}

type RuleSet struct {
	Id          string
	Description string
	Rules       []Rule
}

type FintConfig struct {
	RuleSets []RuleSet
}

var opt *Opt
var fintConfig *FintConfig
var violationCount int = 0

func getOpts() (*Opt, error) {
	srcRoot := flag.String("s", ".", "Project source root dir")
	projName := flag.String("p", "", "Project name")
	configPath := flag.String("c", "conf/config.json", "Config file path")
	locale := flag.String("l", "default", "Message locale")
	flag.Parse()
	opt := &Opt{SrcRoot: *srcRoot, ProjName: *projName, ConfigPath: *configPath, Locale: *locale}
	return opt, nil
}

func loadFintConfig(file []byte) *FintConfig {
	var fc FintConfig
	json.Unmarshal(file, &fc)
	return &fc
}

func checkSourceFile(filename string) int {
	var violationInFile int = 0
	f, err := os.Open(filename)
	if err != nil {
		fmt.Println("Cannot open " + filename)
		return 1
	}
	defer f.Close()
	r := bufio.NewReaderSize(f, BufSize)
	for n := 1; true; n++ {
		lineBytes, isPrefix, err := r.ReadLine()
		if isPrefix {
			fmt.Printf("Too long line: %s", filename)
			return 1
		}
		line := string(lineBytes)
		if err != io.EOF && err != nil {
			fmt.Println(err)
			return 1
		}
		var rs RuleSet = fintConfig.RuleSets[0]
		for i := range rs.Rules {
			if matched, _ := regexp.MatchString(rs.Rules[i].Pattern, line); matched {
				violationInFile++
				fmt.Printf("%s:%d:1: warning: %s\n", filename, n, rs.Rules[i].Message[opt.Locale])
			}
		}
		if err == io.EOF {
			break
		}
	}
	return violationInFile
}

func checkFile(path string, f os.FileInfo, err error) error {
	if matched, _ := regexp.MatchString(".*\\.(m|mm|h)$", path); matched {
		violationCount += checkSourceFile(path)
	}
	return nil
}

func pluralize(value int, singular, plural string) string {
	if value < 2 {
		return singular
	}
	return plural
}

func main() {
	var err error
	opt, err = getOpts()
	if err != nil {
		log.Print(err)
		os.Exit(1)
	}

	conf, err := ioutil.ReadFile(opt.ConfigPath)
	if err != nil {
		fmt.Println("Config file not found.")
		os.Exit(1)
	}
	fintConfig = loadFintConfig(conf)

	os.Chdir(opt.SrcRoot)
	err = filepath.Walk(opt.ProjName, checkFile)

	if 0 < violationCount {
		fmt.Printf("\n%d %s generated.\n",
			violationCount, pluralize(violationCount, "warning", "warnings"))
		os.Exit(1)
	}
}
<commit_msg>Fixed case of a package private constant.<commit_after>/*
 Copyright (c) 2014 Soichiro Kashima
*/
package main

import (
	"bufio"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"regexp"
)

const bufSize = 4096

type Opt struct {
	SrcRoot    string
	ProjName   string
	ConfigPath string
	Locale     string
}

type Rule struct {
	Pattern string
	Message map[string]string
}

type RuleSet struct {
	Id          string
	Description string
	Rules       []Rule
}

type FintConfig struct {
	RuleSets []RuleSet
}

var opt *Opt
var fintConfig *FintConfig
var violationCount int = 0

func getOpts() (*Opt, error) {
	srcRoot := flag.String("s", ".", "Project source root dir")
	projName := flag.String("p", "", "Project name")
	configPath := flag.String("c", "conf/config.json", "Config file path")
	locale := flag.String("l", "default", "Message locale")
	flag.Parse()
	opt := &Opt{SrcRoot: *srcRoot, ProjName: *projName, ConfigPath: *configPath, Locale: *locale}
	return opt, nil
}

func loadFintConfig(file []byte) *FintConfig {
	var fc FintConfig
	json.Unmarshal(file, &fc)
	return &fc
}

func checkSourceFile(filename string) int {
	var violationInFile int = 0
	f, err := os.Open(filename)
	if err != nil {
		fmt.Println("Cannot open " + filename)
		return 1
	}
	defer f.Close()
	r := bufio.NewReaderSize(f, bufSize)
	for n := 1; true; n++ {
		lineBytes, isPrefix, err := r.ReadLine()
		if isPrefix {
			fmt.Printf("Too long line: %s", filename)
			return 1
		}
		line := string(lineBytes)
		if err != io.EOF && err != nil {
			fmt.Println(err)
			return 1
		}
		var rs RuleSet = fintConfig.RuleSets[0]
		for i := range rs.Rules {
			if matched, _ := regexp.MatchString(rs.Rules[i].Pattern, line); matched {
				violationInFile++
				fmt.Printf("%s:%d:1: warning: %s\n", filename, n, rs.Rules[i].Message[opt.Locale])
			}
		}
		if err == io.EOF {
			break
		}
	}
	return violationInFile
}

func checkFile(path string, f os.FileInfo, err error) error {
	if matched, _ := regexp.MatchString(".*\\.(m|mm|h)$", path); matched {
		violationCount += checkSourceFile(path)
	}
	return nil
}

func pluralize(value int, singular, plural string) string {
	if value < 2 {
		return singular
	}
	return plural
}

func main() {
	var err error
	opt, err = getOpts()
	if err != nil {
		log.Print(err)
		os.Exit(1)
	}

	conf, err := ioutil.ReadFile(opt.ConfigPath)
	if err != nil {
		fmt.Println("Config file not found.")
		os.Exit(1)
	}
	fintConfig = loadFintConfig(conf)

	os.Chdir(opt.SrcRoot)
	err = filepath.Walk(opt.ProjName, checkFile)

	if 0 < violationCount {
		fmt.Printf("\n%d %s generated.\n",
			violationCount, pluralize(violationCount, "warning", "warnings"))
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>// go - simulate occasional problematic (long blocking) requests for disk IO
// to run: go http-simulated-disk-io-server.go

package main

import (
    "bufio"
    "fmt"
    "net"
    "os"
    "strconv"
    "strings"
    "time"
)

type ThreadRequest struct {
    client_socket net.Conn
    receipt_timestamp int64
}

const (
    READ_TIMEOUT_SECS = 4

    HTTP_STATUS_OK = 200
    HTTP_STATUS_TIMEOUT = 408
    HTTP_STATUS_BAD_REQUEST = 400
)

func current_time_millis() int64 {
    return time.Now().UnixNano() / int64(time.Millisecond)
}

func simulated_file_read(disk_read_time_ms int64) {
    time.Sleep(time.Millisecond * time.Duration(disk_read_time_ms))
}

func handle_socket_request(thread_request *ThreadRequest) {
    // read request from client
    request_text, errs :=
        bufio.NewReader(thread_request.client_socket).ReadString('\n')

    // read from socket succeeded?
    if errs == nil {
        len_req_payload := len(request_text)
        // did we get anything from client?
        if len_req_payload > 0 {
            // determine how long the request has waited in queue
            var start_processing_timestamp = current_time_millis()
            var server_queue_time_ms =
                start_processing_timestamp - thread_request.receipt_timestamp
            var server_queue_time_secs = server_queue_time_ms / 1000

            var rc = HTTP_STATUS_OK
            var disk_read_time_ms = int64(0)
            var file_path = ""

            // has this request already timed out?
            if server_queue_time_secs >= READ_TIMEOUT_SECS {
                fmt.Println("timeout (queue)")
                rc = HTTP_STATUS_TIMEOUT
            } else {
                var tokens = strings.Split(request_text, ",")
                if len(tokens) == 3 {
                    rc_input, errs := strconv.Atoi(tokens[0])
                    if errs == nil {
                        req_response_time_ms, errs :=
                            strconv.ParseInt(tokens[1], 10, 64)
                        if errs == nil {
                            rc = rc_input
                            disk_read_time_ms = req_response_time_ms
                            file_path = tokens[2]
                            simulated_file_read(disk_read_time_ms)
                        }
                    }
                }
            }

            // total request time is sum of time spent in queue and the
            // simulated disk read time
            tot_request_time_ms :=
                server_queue_time_ms + disk_read_time_ms

            // create response text
            var response_text = fmt.Sprintf("%d,%d,%s",
                rc,
                tot_request_time_ms,
                file_path)

            // return response to client
            thread_request.client_socket.Write([]byte(response_text))
        }
    }

    // close client socket connection
    thread_request.client_socket.Close()
}

func main() {
    var server_port = "7000"
    //TODO: set listen backlog?
    server_socket, err := net.Listen("tcp", "localhost:" + server_port)

    if err != nil {
        fmt.Println("error: unable to create server socket on port " +
                    server_port)
        fmt.Println(fmt.Sprintf("%v", err))
        os.Exit(1)
    } else {
        //defer server_socket.Body.Close()
        fmt.Println("server listening on port " + server_port)

        for {
            // wait for next socket connection from a client
            client_socket, err := server_socket.Accept()
            if err == nil {
                var thread_request = new(ThreadRequest)
                thread_request.receipt_timestamp = current_time_millis()
                thread_request.client_socket = client_socket
                go handle_socket_request(thread_request)
            }
        }
        os.Exit(0)
    }
}

<commit_msg>complete go http<commit_after>// go - simulate occasional problematic (long blocking) requests for disk IO
// install tools:
//   sudo curl -O https://storage.googleapis.com/golang/go1.6.linux-amd64.tar.gz
//   tar xf go1.6.linux-amd64.tar.gz
//   sudo mv go /usr/local
//   export PATH=/usr/local/go/bin:$PATH
// to build: go build http-simulated-disk-io-server.go
// to run: go run http-simulated-disk-io-server.go

package main

import (
    "bufio"
    "fmt"
    "net"
    "os"
    "strconv"
    "strings"
    "time"
)

type ThreadRequest struct {
    client_socket net.Conn
    receipt_timestamp int64
}

const (
    SERVER_PORT = 7000
    QUEUE_TIMEOUT_SECS = 4
    LISTEN_BACKLOG = 500

    SERVER_NAME = "http-simulated-disk-io-server.go"
    HTTP_STATUS_OK = "200 OK"
    HTTP_STATUS_TIMEOUT = "408 TIMEOUT"
    HTTP_STATUS_BAD_REQUEST = "400 BAD REQUEST"
)

func current_time_millis() int64 {
    return time.Now().UnixNano() / int64(time.Millisecond)
}

func simulated_file_read(disk_read_time_ms int64) {
    time.Sleep(time.Millisecond * time.Duration(disk_read_time_ms))
}

func handle_socket_request(thread_request *ThreadRequest) {
    // read request from client
    request_line, errs :=
        bufio.NewReader(thread_request.client_socket).ReadString('\n')
    var rc = HTTP_STATUS_BAD_REQUEST
    var tot_request_time_ms = int64(0)
    var file_path = ""

    // read from socket succeeded?
    if errs == nil {
        len_req_payload := len(request_line)
        // did we get anything from client?
        if len_req_payload > 0 {
            // determine how long the request has waited in queue
            var start_processing_timestamp = current_time_millis()
            var server_queue_time_ms =
                start_processing_timestamp - thread_request.receipt_timestamp
            var server_queue_time_secs = server_queue_time_ms / 1000

            var disk_read_time_ms = int64(0)

            // has this request already timed out?
            if server_queue_time_secs >= QUEUE_TIMEOUT_SECS {
                fmt.Println("timeout (queue)")
                rc = HTTP_STATUS_TIMEOUT
            } else {
                var line_tokens = strings.Split(request_line, " ")
                if len(line_tokens) == 2 {
                    //request_method := line_tokens[0]
                    args_text := line_tokens[1]
                    var fields = strings.Split(args_text, ",")
                    if len(fields) == 3 {
                        //rc_input, errs := strconv.Atoi(fields[0])
                        //if errs == nil {
                        req_response_time_ms, errs :=
                            strconv.ParseInt(fields[1], 10, 64)
                        if errs == nil {
                            //rc = rc_input
                            disk_read_time_ms = req_response_time_ms
                            file_path = fields[2]
                            simulated_file_read(disk_read_time_ms)
                            rc = HTTP_STATUS_OK
                        }
                        //}
                    }
                }
            }

            // total request time is sum of time spent in queue and the
            // simulated disk read time
            tot_request_time_ms =
                server_queue_time_ms + disk_read_time_ms
        }
    }

    // create response
    var response_body = fmt.Sprintf("%d,%s",
       tot_request_time_ms,
       file_path)

    var response_headers = fmt.Sprintf("HTTP/1.1 %s\n" +
                                       "Server: %s\n" +
                                       "Content-Length: %d\n" +
                                       "Connection: close\n" +
                                       "\n",
                                       rc,
                                       SERVER_NAME,
                                       len(response_body))

    // return response to client
    thread_request.client_socket.Write([]byte(response_headers))
    thread_request.client_socket.Write([]byte(response_body))

    // close client socket connection
    thread_request.client_socket.Close()
}

func main() {
    //TODO: set listen backlog
    //TODO: set reuse addr
    server_socket, err := net.Listen("tcp",
                                     fmt.Sprintf("localhost:%d", SERVER_PORT))

    if err != nil {
        fmt.Println("error: unable to create server socket on port %s",
                    SERVER_PORT)
        fmt.Println(fmt.Sprintf("%v", err))
        os.Exit(1)
    } else {
        //defer server_socket.Body.Close()
        fmt.Println(fmt.Sprintf("server (%s) listening on port %d",
                                SERVER_NAME,
                                SERVER_PORT))

        for {
            // wait for next socket connection from a client
            client_socket, err := server_socket.Accept()
            if err == nil {
                var thread_request = new(ThreadRequest)
                thread_request.receipt_timestamp = current_time_millis()
                thread_request.client_socket = client_socket
                go handle_socket_request(thread_request)
            }
        }
        os.Exit(0)
    }
}

<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"path"
)

func init() {
	log.SetFlags(0)
}

func fetch(url string) (filename string, n int64, err error) {
	resp, err := http.Get(url)
	if err != nil {
		return "", 0, err
	}
	defer resp.Body.Close()

	local := path.Base(resp.Request.URL.Path)
	if local == "/" {
		local = "index.html"
	}
	f, err := os.Create(local)
	if err != nil {
		return "", 0, err
	}
	// NOTE: not sure this is correct
	defer func() {
		// Close file, but prefer error from Copy, if any.
		if closeErr := f.Close(); err == nil {
			err = closeErr
		}
	}()
	n, err = io.Copy(f, resp.Body)
	return local, n, err
}

func main() {
	if len(os.Args) < 2 {
		log.Fatalln("please, specify URL to fetch")
	}
	filename, copied, err := fetch(os.Args[1])
	if err != nil {
		log.Fatalln(err)
	}
	fmt.Printf("%d bytes were copied to %s\n", copied, filename)
}
<commit_msg>[5.18] Removed commemt.<commit_after>package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"path"
)

func init() {
	log.SetFlags(0)
}

func fetch(url string) (filename string, n int64, err error) {
	resp, err := http.Get(url)
	if err != nil {
		return "", 0, err
	}
	defer resp.Body.Close()

	local := path.Base(resp.Request.URL.Path)
	if local == "/" {
		local = "index.html"
	}
	f, err := os.Create(local)
	if err != nil {
		return "", 0, err
	}

	defer func() {
		// Close file, but prefer error from Copy, if any.
		if closeErr := f.Close(); err == nil {
			err = closeErr
		}
	}()
	n, err = io.Copy(f, resp.Body)
	return local, n, err
}

func main() {
	if len(os.Args) < 2 {
		log.Fatalln("please, specify URL to fetch")
	}
	filename, copied, err := fetch(os.Args[1])
	if err != nil {
		log.Fatalln(err)
	}
	fmt.Printf("%d bytes were copied to %s\n", copied, filename)
}
<|endoftext|>


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

package cmd

import "github.com/skyrings/skyring-common/tools/uuid"

// Checks on GetObject arguments, bucket and object.
func checkGetObjArgs(bucket, object string) error {
	return checkBucketAndObjectNames(bucket, object)
}

// Checks on DeleteObject arguments, bucket and object.
func checkDelObjArgs(bucket, object string) error {
	return checkBucketAndObjectNames(bucket, object)
}

// Checks bucket and object name validity, returns nil if both are valid.
func checkBucketAndObjectNames(bucket, object string) error {
	// Verify if bucket is valid.
	if !IsValidBucketName(bucket) {
		return traceError(BucketNameInvalid{Bucket: bucket})
	}
	// Verify if object is valid.
	if !IsValidObjectName(object) {
		return traceError(ObjectNameInvalid{Bucket: bucket, Object: object})
	}
	return nil
}

// Checks for all ListObjects arguments validity.
func checkListObjsArgs(bucket, prefix, marker, delimiter string, obj ObjectLayer) error {
	// Verify if bucket exists before validating object name.
	// This is done on purpose since the order of errors is
	// important here bucket does not exist error should
	// happen before we return an error for invalid object name.
	// FIXME: should be moved to handler layer.
	if err := checkBucketExist(bucket, obj); err != nil {
		return traceError(err)
	}
	// Validates object prefix validity after bucket exists.
	if !IsValidObjectPrefix(prefix) {
		return traceError(ObjectNameInvalid{
			Bucket: bucket,
			Object: prefix,
		})
	}
	// Verify if delimiter is anything other than '/', which we do not support.
	if delimiter != "" && delimiter != slashSeparator {
		return traceError(UnsupportedDelimiter{
			Delimiter: delimiter,
		})
	}
	// Verify if marker has prefix.
	if marker != "" && !hasPrefix(marker, prefix) {
		return traceError(InvalidMarkerPrefixCombination{
			Marker: marker,
			Prefix: prefix,
		})
	}
	return nil
}

// Checks for all ListMultipartUploads arguments validity.
func checkListMultipartArgs(bucket, prefix, keyMarker, uploadIDMarker, delimiter string, obj ObjectLayer) error {
	if err := checkListObjsArgs(bucket, prefix, keyMarker, delimiter, obj); err != nil {
		return err
	}
	if uploadIDMarker != "" {
		if hasSuffix(keyMarker, slashSeparator) {
			return traceError(InvalidUploadIDKeyCombination{
				UploadIDMarker: uploadIDMarker,
				KeyMarker:      keyMarker,
			})
		}
		id, err := uuid.Parse(uploadIDMarker)
		if err != nil {
			return traceError(err)
		}
		if id.IsZero() {
			return traceError(MalformedUploadID{
				UploadID: uploadIDMarker,
			})
		}
	}
	return nil
}

// Checks for NewMultipartUpload arguments validity, also validates if bucket exists.
func checkNewMultipartArgs(bucket, object string, obj ObjectLayer) error {
	return checkPutObjectArgs(bucket, object, obj)
}

// Checks for PutObjectPart arguments validity, also validates if bucket exists.
func checkPutObjectPartArgs(bucket, object string, obj ObjectLayer) error {
	return checkPutObjectArgs(bucket, object, obj)
}

// Checks for ListParts arguments validity, also validates if bucket exists.
func checkListPartsArgs(bucket, object string, obj ObjectLayer) error {
	return checkPutObjectArgs(bucket, object, obj)
}

// Checks for CompleteMultipartUpload arguments validity, also validates if bucket exists.
func checkCompleteMultipartArgs(bucket, object string, obj ObjectLayer) error {
	return checkPutObjectArgs(bucket, object, obj)
}

// Checks for AbortMultipartUpload arguments validity, also validates if bucket exists.
func checkAbortMultipartArgs(bucket, object string, obj ObjectLayer) error {
	return checkPutObjectArgs(bucket, object, obj)
}

// Checks for PutObject arguments validity, also validates if bucket exists.
func checkPutObjectArgs(bucket, object string, obj ObjectLayer) error {
	// Verify if bucket exists before validating object name.
	// This is done on purpose since the order of errors is
	// important here bucket does not exist error should
	// happen before we return an error for invalid object name.
	// FIXME: should be moved to handler layer.
	if err := checkBucketExist(bucket, obj); err != nil {
		return traceError(err)
	}
	// Validates object name validity after bucket exists.
	if !IsValidObjectName(object) {
		return traceError(ObjectNameInvalid{
			Bucket: bucket,
			Object: object,
		})
	}
	return nil
}

// Checks whether bucket exists and returns appropriate error if not.
func checkBucketExist(bucket string, obj ObjectLayer) error {
	if !IsValidBucketName(bucket) {
		return BucketNameInvalid{Bucket: bucket}
	}
	_, err := obj.GetBucketInfo(bucket)
	if err != nil {
		return BucketNotFound{Bucket: bucket}
	}
	return nil
}
<commit_msg>obj: Make checkBucketExist() returns all errors (#3843)<commit_after>/*
 * Minio Cloud Storage, (C) 2016 Minio, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cmd

import "github.com/skyrings/skyring-common/tools/uuid"

// Checks on GetObject arguments, bucket and object.
func checkGetObjArgs(bucket, object string) error {
	return checkBucketAndObjectNames(bucket, object)
}

// Checks on DeleteObject arguments, bucket and object.
func checkDelObjArgs(bucket, object string) error {
	return checkBucketAndObjectNames(bucket, object)
}

// Checks bucket and object name validity, returns nil if both are valid.
func checkBucketAndObjectNames(bucket, object string) error {
	// Verify if bucket is valid.
	if !IsValidBucketName(bucket) {
		return traceError(BucketNameInvalid{Bucket: bucket})
	}
	// Verify if object is valid.
	if !IsValidObjectName(object) {
		return traceError(ObjectNameInvalid{Bucket: bucket, Object: object})
	}
	return nil
}

// Checks for all ListObjects arguments validity.
func checkListObjsArgs(bucket, prefix, marker, delimiter string, obj ObjectLayer) error {
	// Verify if bucket exists before validating object name.
	// This is done on purpose since the order of errors is
	// important here bucket does not exist error should
	// happen before we return an error for invalid object name.
	// FIXME: should be moved to handler layer.
	if err := checkBucketExist(bucket, obj); err != nil {
		return traceError(err)
	}
	// Validates object prefix validity after bucket exists.
	if !IsValidObjectPrefix(prefix) {
		return traceError(ObjectNameInvalid{
			Bucket: bucket,
			Object: prefix,
		})
	}
	// Verify if delimiter is anything other than '/', which we do not support.
	if delimiter != "" && delimiter != slashSeparator {
		return traceError(UnsupportedDelimiter{
			Delimiter: delimiter,
		})
	}
	// Verify if marker has prefix.
	if marker != "" && !hasPrefix(marker, prefix) {
		return traceError(InvalidMarkerPrefixCombination{
			Marker: marker,
			Prefix: prefix,
		})
	}
	return nil
}

// Checks for all ListMultipartUploads arguments validity.
func checkListMultipartArgs(bucket, prefix, keyMarker, uploadIDMarker, delimiter string, obj ObjectLayer) error {
	if err := checkListObjsArgs(bucket, prefix, keyMarker, delimiter, obj); err != nil {
		return err
	}
	if uploadIDMarker != "" {
		if hasSuffix(keyMarker, slashSeparator) {
			return traceError(InvalidUploadIDKeyCombination{
				UploadIDMarker: uploadIDMarker,
				KeyMarker:      keyMarker,
			})
		}
		id, err := uuid.Parse(uploadIDMarker)
		if err != nil {
			return traceError(err)
		}
		if id.IsZero() {
			return traceError(MalformedUploadID{
				UploadID: uploadIDMarker,
			})
		}
	}
	return nil
}

// Checks for NewMultipartUpload arguments validity, also validates if bucket exists.
func checkNewMultipartArgs(bucket, object string, obj ObjectLayer) error {
	return checkPutObjectArgs(bucket, object, obj)
}

// Checks for PutObjectPart arguments validity, also validates if bucket exists.
func checkPutObjectPartArgs(bucket, object string, obj ObjectLayer) error {
	return checkPutObjectArgs(bucket, object, obj)
}

// Checks for ListParts arguments validity, also validates if bucket exists.
func checkListPartsArgs(bucket, object string, obj ObjectLayer) error {
	return checkPutObjectArgs(bucket, object, obj)
}

// Checks for CompleteMultipartUpload arguments validity, also validates if bucket exists.
func checkCompleteMultipartArgs(bucket, object string, obj ObjectLayer) error {
	return checkPutObjectArgs(bucket, object, obj)
}

// Checks for AbortMultipartUpload arguments validity, also validates if bucket exists.
func checkAbortMultipartArgs(bucket, object string, obj ObjectLayer) error {
	return checkPutObjectArgs(bucket, object, obj)
}

// Checks for PutObject arguments validity, also validates if bucket exists.
func checkPutObjectArgs(bucket, object string, obj ObjectLayer) error {
	// Verify if bucket exists before validating object name.
	// This is done on purpose since the order of errors is
	// important here bucket does not exist error should
	// happen before we return an error for invalid object name.
	// FIXME: should be moved to handler layer.
	if err := checkBucketExist(bucket, obj); err != nil {
		return traceError(err)
	}
	// Validates object name validity after bucket exists.
	if !IsValidObjectName(object) {
		return traceError(ObjectNameInvalid{
			Bucket: bucket,
			Object: object,
		})
	}
	return nil
}

// Checks whether bucket exists and returns appropriate error if not.
func checkBucketExist(bucket string, obj ObjectLayer) error {
	if !IsValidBucketName(bucket) {
		return BucketNameInvalid{Bucket: bucket}
	}
	_, err := obj.GetBucketInfo(bucket)
	if err != nil {
		return errorCause(err)
	}
	return nil
}
<|endoftext|>


<commit_before>package analysis

import (
	"fmt"
	"reflect"
	"unicode"
)

// Validate reports an error if any of the analyzers are misconfigured.
// Checks include:
// that the name is a valid identifier;
// that analyzer names are unique;
// that the Requires graph is acyclic;
// that analyzer fact types are unique;
// that each fact type is a pointer.
func Validate(analyzers []*Analyzer) error {
	names := make(map[string]bool)

	// Map each fact type to its sole generating analyzer.
	factTypes := make(map[reflect.Type]*Analyzer)

	// Traverse the Requires graph, depth first.
	const (
		white = iota
		grey
		black
		finished
	)
	color := make(map[*Analyzer]uint8)
	var visit func(a *Analyzer) error
	visit = func(a *Analyzer) error {
		if a == nil {
			return fmt.Errorf("nil *Analyzer")
		}
		if color[a] == white {
			color[a] = grey

			// names
			if !validIdent(a.Name) {
				return fmt.Errorf("invalid analyzer name %q", a)
			}
			if names[a.Name] {
				return fmt.Errorf("duplicate analyzer name %q", a)
			}
			names[a.Name] = true

			if a.Doc == "" {
				return fmt.Errorf("analyzer %q is undocumented", a)
			}

			// fact types
			for _, f := range a.FactTypes {
				if f == nil {
					return fmt.Errorf("analyzer %s has nil FactType", a)
				}
				t := reflect.TypeOf(f)
				if prev := factTypes[t]; prev != nil {
					return fmt.Errorf("fact type %s registered by two analyzers: %v, %v",
						t, a, prev)
				}
				if t.Kind() != reflect.Ptr {
					return fmt.Errorf("%s: fact type %s is not a pointer", a, t)
				}
				factTypes[t] = a
			}

			// recursion
			for i, req := range a.Requires {
				if err := visit(req); err != nil {
					return fmt.Errorf("%s.Requires[%d]: %v", a.Name, i, err)
				}
			}
			color[a] = black
		}

		return nil
	}
	for _, a := range analyzers {
		if err := visit(a); err != nil {
			return err
		}
	}

	// Reject duplicates among analyzers.
	// Precondition:  color[a] == black.
	// Postcondition: color[a] == finished.
	for _, a := range analyzers {
		if color[a] == finished {
			return fmt.Errorf("duplicate analyzer: %s", a.Name)
		}
		color[a] = finished
	}

	return nil
}

func validIdent(name string) bool {
	for i, r := range name {
		if !(r == '_' || unicode.IsLetter(r) || i > 0 && unicode.IsDigit(r)) {
			return false
		}
	}
	return name != ""
}
<commit_msg>go/analysis: remove requirement for unique Analyzer names<commit_after>package analysis

import (
	"fmt"
	"reflect"
	"unicode"
)

// Validate reports an error if any of the analyzers are misconfigured.
// Checks include:
// that the name is a valid identifier;
// that the Requires graph is acyclic;
// that analyzer fact types are unique;
// that each fact type is a pointer.
func Validate(analyzers []*Analyzer) error {
	// Map each fact type to its sole generating analyzer.
	factTypes := make(map[reflect.Type]*Analyzer)

	// Traverse the Requires graph, depth first.
	const (
		white = iota
		grey
		black
		finished
	)
	color := make(map[*Analyzer]uint8)
	var visit func(a *Analyzer) error
	visit = func(a *Analyzer) error {
		if a == nil {
			return fmt.Errorf("nil *Analyzer")
		}
		if color[a] == white {
			color[a] = grey

			// names
			if !validIdent(a.Name) {
				return fmt.Errorf("invalid analyzer name %q", a)
			}

			if a.Doc == "" {
				return fmt.Errorf("analyzer %q is undocumented", a)
			}

			// fact types
			for _, f := range a.FactTypes {
				if f == nil {
					return fmt.Errorf("analyzer %s has nil FactType", a)
				}
				t := reflect.TypeOf(f)
				if prev := factTypes[t]; prev != nil {
					return fmt.Errorf("fact type %s registered by two analyzers: %v, %v",
						t, a, prev)
				}
				if t.Kind() != reflect.Ptr {
					return fmt.Errorf("%s: fact type %s is not a pointer", a, t)
				}
				factTypes[t] = a
			}

			// recursion
			for i, req := range a.Requires {
				if err := visit(req); err != nil {
					return fmt.Errorf("%s.Requires[%d]: %v", a.Name, i, err)
				}
			}
			color[a] = black
		}

		return nil
	}
	for _, a := range analyzers {
		if err := visit(a); err != nil {
			return err
		}
	}

	// Reject duplicates among analyzers.
	// Precondition:  color[a] == black.
	// Postcondition: color[a] == finished.
	for _, a := range analyzers {
		if color[a] == finished {
			return fmt.Errorf("duplicate analyzer: %s", a.Name)
		}
		color[a] = finished
	}

	return nil
}

func validIdent(name string) bool {
	for i, r := range name {
		if !(r == '_' || unicode.IsLetter(r) || i > 0 && unicode.IsDigit(r)) {
			return false
		}
	}
	return name != ""
}
<|endoftext|>


<commit_before>package images

import (
	"path/filepath"
)

/*
* Preprocess image files on client side.
* 1. possibly adjust the orientation
* 2. resize the image to a width or height limit
* 3. remove the exif data
* Call this function on any file uploaded to weedfs
*
*/
func MaybePreprocessImage(filename string, data []byte, width, height int) (resized []byte, w int, h int) {
	ext := filepath.Ext(filename)
	switch ext {
	case ".png", ".gif":
		return Resized(ext, data, width, height)
	case ".jpg", ".jpeg":
	  data = FixJpgOrientation(data)
		return Resized(ext, data, width, height)
	}
	return data, 0, 0
}
<commit_msg>lowercase when checking file name extensions.<commit_after>package images

import (
	"path/filepath"
	"strings"
)

/*
* Preprocess image files on client side.
* 1. possibly adjust the orientation
* 2. resize the image to a width or height limit
* 3. remove the exif data
* Call this function on any file uploaded to weedfs
*
 */
func MaybePreprocessImage(filename string, data []byte, width, height int) (resized []byte, w int, h int) {
	ext := filepath.Ext(filename)
	ext = strings.ToLower(ext)
	switch ext {
	case ".png", ".gif":
		return Resized(ext, data, width, height)
	case ".jpg", ".jpeg":
		data = FixJpgOrientation(data)
		return Resized(ext, data, width, height)
	}
	return data, 0, 0
}
<|endoftext|>


<commit_before>package markdown

import (
	"fmt"
	"strings"
)

// Render translates markdown to HTML
func Render(markdown string) (html string) {
	markdown = handleBold(markdown)
	markdown = handleItalic(markdown)
	if isHeader(markdown) {
		return handleHeader(markdown)
	}
	return wrapParagraph(markdown)
}

func wrapParagraph(s string) string {
	return "
" + s + "
"
}

func handleBold(markdown string) string {
	markdown = strings.Replace(markdown, "__", "", 1)
	markdown = strings.Replace(markdown, "__", "", 1)
	return markdown
}

func handleItalic(markdown string) string {
	markdown = strings.Replace(markdown, "_", "", 1)
	markdown = strings.Replace(markdown, "_", "", 1)
	return markdown
}

func isHeader(line string) bool {
	return strings.HasPrefix(line, "#")
}

func handleHeader(line string) string {
	if strings.HasPrefix(line, "######") {
		return handleHeaderWithLevel(line, 6)
	}
	if strings.HasPrefix(line, "#####") {
		return handleHeaderWithLevel(line, 5)
	}
	if strings.HasPrefix(line, "####") {
		return handleHeaderWithLevel(line, 4)
	}
	if strings.HasPrefix(line, "###") {
		return handleHeaderWithLevel(line, 3)
	}
	if strings.HasPrefix(line, "##") {
		return handleHeaderWithLevel(line, 2)
	}
	if strings.HasPrefix(line, "#") {
		return handleHeaderWithLevel(line, 1)
	}
	panic("handleHeader called with a line that doesn't contain a header")
}

func handleHeaderWithLevel(markdown string, level int) string {
	return fmt.Sprintf("<h%d>%s</h%d>", level, markdown[level+1:], level)
}
<commit_msg>Solve Markdown<commit_after>package markdown

import (
	"fmt"
	"strings"
)

// Render translates markdown to HTML
func Render(markdown string) (html string) {
	markdown = handleBold(markdown)
	markdown = handleItalic(markdown)
	lines := strings.Split(markdown, "\n")
	htmlLines := []string{}
	for _, line := range lines {
		htmlLines = append(htmlLines, render(line))
	}
	result := strings.Join(htmlLines, "")
	return handleUnorderedList(result)
}

func render(line string) (html string) {
	if isHeader(line) {
		return handleHeader(line)
	}
	if isListItem(line) {
		return handleListItem(line)
	}
	return handleParagraph(line)
}

func isListItem(line string) bool {
	return strings.HasPrefix(line, "*")
}

func isHeader(line string) bool {
	return strings.HasPrefix(line, "#")
}

func handleListItem(line string) string {
	return fmt.Sprintf("
%s
", line[2:])
}

func handleHeader(line string) string {
	if strings.HasPrefix(line, "######") {
		return handleHeaderWithLevel(line, 6)
	}
	if strings.HasPrefix(line, "#####") {
		return handleHeaderWithLevel(line, 5)
	}
	if strings.HasPrefix(line, "####") {
		return handleHeaderWithLevel(line, 4)
	}
	if strings.HasPrefix(line, "###") {
		return handleHeaderWithLevel(line, 3)
	}
	if strings.HasPrefix(line, "##") {
		return handleHeaderWithLevel(line, 2)
	}
	if strings.HasPrefix(line, "#") {
		return handleHeaderWithLevel(line, 1)
	}
	panic("handleHeader called with a line that doesn't contain a header")
}

func handleHeaderWithLevel(markdown string, level int) string {
	return fmt.Sprintf("<h%d>%s</h%d>", level, markdown[level+1:], level)
}

func handleParagraph(line string) string {
	return fmt.Sprintf("
%s
", line)
}

func handleBold(markdown string) string {
	markdown = strings.Replace(markdown, "__", "", 1)
	markdown = strings.Replace(markdown, "__", "", 1)
	return markdown
}

func handleItalic(markdown string) string {
	markdown = strings.Replace(markdown, "_", "", 1)
	markdown = strings.Replace(markdown, "_", "", 1)
	return markdown
}

func handleUnorderedList(line string) string {
	// HACKHACK this assumes there is only one consecutive unordered list in the input
	line = strings.Replace(line, "
", "

", 1)
	line = replaceLast(line, "
", "

", 1)
	return line

}

func replaceLast(line string, search string, replacement string, occurences int) string {
	i := strings.LastIndex(line, search)
	if i == -1 {
		return line
	}
	return line[:i] + strings.Replace(line[i:], search, replacement, 1)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/spf13/cobra"
	"os"
	"path/filepath"
)

var checkCmd = &cobra.Command{
	Use:               "check",
	Aliases:           []string{"validate"},
	Short:             "Validates the existing noodles.toml",
	Long:              "Validates the existing noodles.toml",
	Run:               check,
	DisableAutoGenTag: true,
}

// check will validate noodles.toml
func check(cmd *cobra.Command, args []string) {
	if conf, readErr := ReadConfig(filepath.Join(workdir, "noodles.toml")); readErr == nil { // Read the config
		noodles = conf
	} else {
		fmt.Printf("noodles.toml appears to have the following issue(s):\n%s\n", readErr.Error())
		os.Exit(1)
	}

	for name, project := range noodles.Projects { // For each project
		var plugin NoodlesPlugin
		fmt.Printf("Checking %s:\n", name)

		if project.Plugin != "" {
			switch project.Plugin {
			case "go": // Go
				plugin = &goPlugin
				break
			case "less": // LESS
				plugin = &lessPlugin
				break
			case "typescript": // TypeScript
				plugin = &typescriptPlugin
				break
			default:
				fmt.Printf("%s is not a valid plugin.\n", project.Plugin)
				os.Exit(1)
			}

			results := plugin.Check(&project) // Check the project, return our check results
			resultsTypes := []string{"Deprecations", "Errors", "Recommendation"}

			for _, resultType := range resultsTypes {
				if resultList, exists := results[resultType]; exists { // This type exists
					if len(resultList) != 0 { // There are items in this type
						fmt.Printf("\t%s:\n", resultType)
						for _, item := range resultList { // For each item
							fmt.Printf("\t\t❌ %s\n", item)
						}
					} else {
						fmt.Printf("\t%s: None\n", resultType)
					}
				} else {
					fmt.Printf("\t%s: None\n", resultType)
				}
			}

			fmt.Println()
		} else if project.Plugin == "" && project.Requires == nil { // No Plugin or Requires are set
			fmt.Printf("%s is missing a plugin definition.\n", name)
		}
	}
}
<commit_msg>Use trunk in check functionality<commit_after>package main

import (
	"fmt"
	"github.com/JoshStrobl/trunk"
	"github.com/spf13/cobra"
	"os"
	"path/filepath"
	"strconv"
)

var checkCmd = &cobra.Command{
	Use:               "check",
	Aliases:           []string{"validate"},
	Short:             "Validates the existing noodles.toml",
	Long:              "Validates the existing noodles.toml",
	Run:               check,
	DisableAutoGenTag: true,
}

// check will validate noodles.toml
func check(cmd *cobra.Command, args []string) {
	if conf, readErr := ReadConfig(filepath.Join(workdir, "noodles.toml")); readErr == nil { // Read the config
		noodles = conf
	} else {
		fmt.Printf("noodles.toml appears to have the following issue(s):\n%s\n", readErr.Error())
		os.Exit(1)
	}

	for name, project := range noodles.Projects { // For each project
		var plugin NoodlesPlugin
		trunk.LogInfo("Checking " + name)

		if project.Plugin != "" {
			switch project.Plugin {
			case "go": // Go
				plugin = &goPlugin
				break
			case "less": // LESS
				plugin = &lessPlugin
				break
			case "typescript": // TypeScript
				plugin = &typescriptPlugin
				break
			default:
				trunk.LogFatal(project.Plugin + "is not a valid plugin.")
			}

			results := plugin.Check(&project) // Check the project, return our check results
			resultsTypes := []string{"Deprecations", "Errors", "Recommendations"}

			for _, resultType := range resultsTypes {
				if resultList, exists := results[resultType]; exists { // This type exists
					if len(resultList) != 0 { // There are items in this type
						header := fmt.Sprintf("%s (%s)", resultType, strconv.Itoa(len(resultList)))

						if resultType == "Deprecations" { // Deprecations
							trunk.LogWarn(header)
						} else if resultType == "Errors" { // Errors
							trunk.LogErr(header)
						} else if resultType == "Recommendations" { // Recommendations
							trunk.LogInfo(header)
						}

						for _, item := range resultList { // For each item
							fmt.Println(item)
						}
					} else {
						trunk.LogSuccess(fmt.Sprintf("%s: None", resultType))
					}
				} else {
					trunk.LogSuccess(fmt.Sprintf("%s: None", resultType))
				}
			}

			fmt.Println()
		} else if project.Plugin == "" && project.Requires == nil { // No Plugin or Requires are set
			trunk.LogErr(name + " is missing a plugin definition.")
		}
	}
}
<|endoftext|>