<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/sirupsen/logrus"

	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/test-infra/prow/config"
	"k8s.io/test-infra/prow/tide"
	"k8s.io/test-infra/prow/tide/history"
)

type tidePools struct {
	Queries     []string
	TideQueries []config.TideQuery
	Pools       []tide.Pool
}

type tideHistory struct {
	History map[string][]history.Record
}

type tideAgent struct {
	log          *logrus.Entry
	path         string
	updatePeriod func() time.Duration

	// Config for hiding repos
	hiddenRepos func() []string
	hiddenOnly  bool
	showHidden  bool

	tenantIDs []string
	cfg       config.Config

	sync.Mutex
	pools   []tide.Pool
	history map[string][]history.Record
}

func (ta *tideAgent) start() {
	startTimePool := time.Now()
	if err := ta.updatePools(); err != nil {
		ta.log.WithError(err).Error("Updating pools the first time.")
	}
	startTimeHistory := time.Now()
	if err := ta.updateHistory(); err != nil {
		ta.log.WithError(err).Error("Updating history the first time.")
	}

	go func() {
		for {
			time.Sleep(time.Until(startTimePool.Add(ta.updatePeriod())))
			startTimePool = time.Now()
			if err := ta.updatePools(); err != nil {
				ta.log.WithError(err).Error("Updating pools.")
			}
		}
	}()
	go func() {
		for {
			time.Sleep(time.Until(startTimeHistory.Add(ta.updatePeriod())))
			startTimeHistory = time.Now()
			if err := ta.updateHistory(); err != nil {
				ta.log.WithError(err).Error("Updating history.")
			}
		}
	}()
}

func fetchTideData(log *logrus.Entry, path string, data interface{}) error {
	var prevErrs []error
	var err error
	backoff := 5 * time.Second
	for i := 0; i < 4; i++ {
		var resp *http.Response
		if err != nil {
			prevErrs = append(prevErrs, err)
			time.Sleep(backoff)
			backoff *= 4
		}
		resp, err = http.Get(path)
		if err == nil {
			defer resp.Body.Close()
			if resp.StatusCode < 200 || resp.StatusCode > 299 {
				err = fmt.Errorf("response has status code %d", resp.StatusCode)
				continue
			}
			if err = json.NewDecoder(resp.Body).Decode(data); err != nil {
				break
			}
			break
		}
	}

	// Either combine previous errors with the returned error, or if we succeeded
	// log once about any errors we saw before succeeding.
	prevErr := utilerrors.NewAggregate(prevErrs)
	if err != nil {
		return utilerrors.NewAggregate([]error{err, prevErr})
	}
	if prevErr != nil {
		log.WithError(prevErr).Infof(
			"Failed %d retries fetching Tide data before success: %v.",
			len(prevErrs),
			prevErr,
		)
	}
	return nil
}

func (ta *tideAgent) updatePools() error {
	var pools []tide.Pool
	if err := fetchTideData(ta.log, ta.path, &pools); err != nil {
		return err
	}
	pools = ta.filterPools(pools)

	ta.Lock()
	defer ta.Unlock()
	ta.pools = pools
	return nil
}

func (ta *tideAgent) updateHistory() error {
	path := strings.TrimSuffix(ta.path, "/") + "/history"
	var history map[string][]history.Record
	if err := fetchTideData(ta.log, path, &history); err != nil {
		return err
	}
	history = ta.filterHistory(history)

	ta.Lock()
	defer ta.Unlock()
	ta.history = history
	return nil
}

func (ta *tideAgent) matchingIDs(ids []string) bool {
	return len(ids) > 0 && sets.String{}.Insert(ta.tenantIDs...).HasAll(ids...)
}

func (ta *tideAgent) filterPools(pools []tide.Pool) []tide.Pool {
	filtered := make([]tide.Pool, 0, len(pools))
	for _, pool := range pools {
		// curIDs are the IDs associated with all PJs in the Pool
		// We want to add the ID associated with the OrgRepo for extra protection
		curIDs := sets.NewString(pool.TenantIDs...)
		orgRepoID := ta.cfg.GetProwJobDefault(pool.Org+"/"+pool.Repo, "*").TenantID
		// If the orgrepo is associated with no tenantID OR the default tenantID we ignore it here.
		// This prevents already IDd pools from getting the default ID assigned to them when their orgrepo is not associated with an OrgRepo.
		// Pools with no tenantID and with default tenantID behave the same, so adding the default ID just causes issues
		if orgRepoID != "" && orgRepoID != config.DefaultTenantID {
			curIDs.Insert(orgRepoID)
		}
		if len(ta.tenantIDs) > 0 && ta.matchingIDs(curIDs.List()) {
			// Deck has tenantIDs and they match with the pool
			filtered = append(filtered, pool)
		} else if len(ta.tenantIDs) == 0 {
			//Deck has no tenantID
			needsHide := matches(pool.Org+"/"+pool.Repo, ta.hiddenRepos())
			if needsHide && (ta.showHidden || ta.hiddenOnly) {
				// Pool is hidden and Deck is showing hidden
				filtered = append(filtered, pool)
			} else if !needsHide && !ta.hiddenOnly && noTenantIDOrDefaultTenantID(curIDs.List()) {
				// Pool is not hidden and has no tenantID and Deck is not hidden only.
				filtered = append(filtered, pool)
			}
		}
	}
	return filtered
}

func noTenantIDOrDefaultTenantID(ids []string) bool {
	for _, id := range ids {
		if id != "" && id != config.DefaultTenantID {
			return false
		}
	}
	return true
}

func recordIDs(records []history.Record) sets.String {
	res := sets.String{}
	for _, record := range records {
		res.Insert(record.TenantIDs...)
	}
	return res
}

func (ta *tideAgent) filterHistory(hist map[string][]history.Record) map[string][]history.Record {
	filtered := make(map[string][]history.Record, len(hist))
	for pool, records := range hist {
		orgRepo := strings.Split(pool, ":")[0]
		curIDs := recordIDs(records).Insert()
		orgRepoID := ta.cfg.GetProwJobDefault(orgRepo, "*").TenantID
		// If the orgrepo is associated with no tenantID OR the default tenantID we ignore it here.
		// This prevents already IDd History from getting the default ID assigned to them when their orgrepo is not associated with an OrgRepo.
		// History with no tenantID and with default tenantID behave the same, so adding the default ID just causes issues
		if orgRepoID != "" && orgRepoID != config.DefaultTenantID {
			curIDs.Insert(orgRepoID)
		}
		if len(ta.tenantIDs) > 0 && ta.matchingIDs(curIDs.List()) {
			// Deck has tenantIDs and they match with the History
			filtered[pool] = records
		} else if len(ta.tenantIDs) == 0 {
			needsHide := matches(orgRepo, ta.hiddenRepos())
			if needsHide && (ta.showHidden || ta.hiddenOnly) {
				filtered[pool] = records
			} else if !needsHide && !ta.hiddenOnly && noTenantIDOrDefaultTenantID(curIDs.List()) {
				filtered[pool] = records
			}
		}
	}
	return filtered
}

func (ta *tideAgent) filterQueries(queries []config.TideQuery) []config.TideQuery {
	filtered := make([]config.TideQuery, 0, len(queries))
	for _, qc := range queries {
		curIDs := qc.TenantIDs(ta.cfg)
		needsHide := false
		if len(ta.tenantIDs) > 0 && ta.matchingIDs(curIDs) {
			// Deck has tenantIDs and they match with the Query
			filtered = append(filtered, qc)
		} else if len(ta.tenantIDs) == 0 {
			for _, repo := range qc.Repos {
				if matches(repo, ta.hiddenRepos()) {
					needsHide = true
					break
				}
			}
			if needsHide && (ta.showHidden || ta.hiddenOnly) {
				// Query is hidden and Deck is showing hidden
				filtered = append(filtered, qc)
			} else if !needsHide && !ta.hiddenOnly && noTenantIDOrDefaultTenantID(curIDs) {
				// Query is not hidden and has no tenantID and Deck is not hidden only.
				filtered = append(filtered, qc)
			}
		}
	}
	return filtered
}

// matches returns whether the provided repo intersects
// with repos. repo has always the "org/repo" format but
// repos can include both orgs and repos.
func matches(repo string, repos []string) bool {
	org := strings.Split(repo, "/")[0]
	for _, r := range repos {
		if r == repo || r == org {
			return true
		}
	}
	return false
}
<commit_msg>Fix indenting on if statements<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/sirupsen/logrus"

	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/test-infra/prow/config"
	"k8s.io/test-infra/prow/tide"
	"k8s.io/test-infra/prow/tide/history"
)

type tidePools struct {
	Queries     []string
	TideQueries []config.TideQuery
	Pools       []tide.Pool
}

type tideHistory struct {
	History map[string][]history.Record
}

type tideAgent struct {
	log          *logrus.Entry
	path         string
	updatePeriod func() time.Duration

	// Config for hiding repos
	hiddenRepos func() []string
	hiddenOnly  bool
	showHidden  bool

	tenantIDs []string
	cfg       config.Config

	sync.Mutex
	pools   []tide.Pool
	history map[string][]history.Record
}

func (ta *tideAgent) start() {
	startTimePool := time.Now()
	if err := ta.updatePools(); err != nil {
		ta.log.WithError(err).Error("Updating pools the first time.")
	}
	startTimeHistory := time.Now()
	if err := ta.updateHistory(); err != nil {
		ta.log.WithError(err).Error("Updating history the first time.")
	}

	go func() {
		for {
			time.Sleep(time.Until(startTimePool.Add(ta.updatePeriod())))
			startTimePool = time.Now()
			if err := ta.updatePools(); err != nil {
				ta.log.WithError(err).Error("Updating pools.")
			}
		}
	}()
	go func() {
		for {
			time.Sleep(time.Until(startTimeHistory.Add(ta.updatePeriod())))
			startTimeHistory = time.Now()
			if err := ta.updateHistory(); err != nil {
				ta.log.WithError(err).Error("Updating history.")
			}
		}
	}()
}

func fetchTideData(log *logrus.Entry, path string, data interface{}) error {
	var prevErrs []error
	var err error
	backoff := 5 * time.Second
	for i := 0; i < 4; i++ {
		var resp *http.Response
		if err != nil {
			prevErrs = append(prevErrs, err)
			time.Sleep(backoff)
			backoff *= 4
		}
		resp, err = http.Get(path)
		if err == nil {
			defer resp.Body.Close()
			if resp.StatusCode < 200 || resp.StatusCode > 299 {
				err = fmt.Errorf("response has status code %d", resp.StatusCode)
				continue
			}
			if err = json.NewDecoder(resp.Body).Decode(data); err != nil {
				break
			}
			break
		}
	}

	// Either combine previous errors with the returned error, or if we succeeded
	// log once about any errors we saw before succeeding.
	prevErr := utilerrors.NewAggregate(prevErrs)
	if err != nil {
		return utilerrors.NewAggregate([]error{err, prevErr})
	}
	if prevErr != nil {
		log.WithError(prevErr).Infof(
			"Failed %d retries fetching Tide data before success: %v.",
			len(prevErrs),
			prevErr,
		)
	}
	return nil
}

func (ta *tideAgent) updatePools() error {
	var pools []tide.Pool
	if err := fetchTideData(ta.log, ta.path, &pools); err != nil {
		return err
	}
	pools = ta.filterPools(pools)

	ta.Lock()
	defer ta.Unlock()
	ta.pools = pools
	return nil
}

func (ta *tideAgent) updateHistory() error {
	path := strings.TrimSuffix(ta.path, "/") + "/history"
	var history map[string][]history.Record
	if err := fetchTideData(ta.log, path, &history); err != nil {
		return err
	}
	history = ta.filterHistory(history)

	ta.Lock()
	defer ta.Unlock()
	ta.history = history
	return nil
}

func (ta *tideAgent) matchingIDs(ids []string) bool {
	return len(ids) > 0 && sets.String{}.Insert(ta.tenantIDs...).HasAll(ids...)
}

func (ta *tideAgent) filterPools(pools []tide.Pool) []tide.Pool {
	filtered := make([]tide.Pool, 0, len(pools))
	for _, pool := range pools {
		// curIDs are the IDs associated with all PJs in the Pool
		// We want to add the ID associated with the OrgRepo for extra protection
		curIDs := sets.NewString(pool.TenantIDs...)
		orgRepoID := ta.cfg.GetProwJobDefault(pool.Org+"/"+pool.Repo, "*").TenantID
		// If the orgrepo is associated with no tenantID OR the default tenantID we ignore it here.
		// This prevents already IDd pools from getting the default ID assigned to them when their orgrepo is not associated with an OrgRepo.
		// Pools with no tenantID and with default tenantID behave the same, so adding the default ID just causes issues
		if orgRepoID != "" && orgRepoID != config.DefaultTenantID {
			curIDs.Insert(orgRepoID)
		}
		if len(ta.tenantIDs) > 0 {
			// Deck has tenantIDs
			if ta.matchingIDs(curIDs.List()) {
				// tenantIDs match with the pool
				filtered = append(filtered, pool)
			}
		} else if needsHide := matches(pool.Org+"/"+pool.Repo, ta.hiddenRepos()); needsHide {
			// Deck has no tenantIDs, and the Pool needs to be hidden
			if ta.showHidden || ta.hiddenOnly {
				// Show hidden or hidden only is true
				filtered = append(filtered, pool)
			}
		} else if !ta.hiddenOnly && noTenantIDOrDefaultTenantID(curIDs.List()) {
			// Pool is not hidden and has no tenantID and Deck is not hidden only.
			filtered = append(filtered, pool)
		}
	}
	return filtered
}

func noTenantIDOrDefaultTenantID(ids []string) bool {
	for _, id := range ids {
		if id != "" && id != config.DefaultTenantID {
			return false
		}
	}
	return true
}

func recordIDs(records []history.Record) sets.String {
	res := sets.String{}
	for _, record := range records {
		res.Insert(record.TenantIDs...)
	}
	return res
}

func (ta *tideAgent) filterHistory(hist map[string][]history.Record) map[string][]history.Record {
	filtered := make(map[string][]history.Record, len(hist))
	for pool, records := range hist {
		orgRepo := strings.Split(pool, ":")[0]
		curIDs := recordIDs(records).Insert()
		orgRepoID := ta.cfg.GetProwJobDefault(orgRepo, "*").TenantID
		// If the orgrepo is associated with no tenantID OR the default tenantID we ignore it here.
		// This prevents already IDd History from getting the default ID assigned to them when their orgrepo is not associated with an OrgRepo.
		// History with no tenantID and with default tenantID behave the same, so adding the default ID just causes issues
		if orgRepoID != "" && orgRepoID != config.DefaultTenantID {
			curIDs.Insert(orgRepoID)
		}
		if len(ta.tenantIDs) > 0 {
			if ta.matchingIDs(curIDs.List()) {
				// Deck has tenantIDs and they match with the History
				filtered[pool] = records
			}
		} else if needsHide := matches(orgRepo, ta.hiddenRepos()); needsHide {
			if ta.showHidden || ta.hiddenOnly {
				filtered[pool] = records
			}
		} else if !ta.hiddenOnly && noTenantIDOrDefaultTenantID(curIDs.List()) {
			filtered[pool] = records
		}
	}
	return filtered
}

func (ta *tideAgent) filterQueries(queries []config.TideQuery) []config.TideQuery {
	filtered := make([]config.TideQuery, 0, len(queries))
	for _, qc := range queries {
		curIDs := qc.TenantIDs(ta.cfg)
		needsHide := false
		if len(ta.tenantIDs) > 0 && ta.matchingIDs(curIDs) {
			// Deck has tenantIDs and they match with the Query
			filtered = append(filtered, qc)
		} else if len(ta.tenantIDs) == 0 {
			for _, repo := range qc.Repos {
				if matches(repo, ta.hiddenRepos()) {
					needsHide = true
					break
				}
			}
			if needsHide && (ta.showHidden || ta.hiddenOnly) {
				// Query is hidden and Deck is showing hidden
				filtered = append(filtered, qc)
			} else if !needsHide && !ta.hiddenOnly && noTenantIDOrDefaultTenantID(curIDs) {
				// Query is not hidden and has no tenantID and Deck is not hidden only.
				filtered = append(filtered, qc)
			}
		}
	}
	return filtered
}

// matches returns whether the provided repo intersects
// with repos. repo has always the "org/repo" format but
// repos can include both orgs and repos.
func matches(repo string, repos []string) bool {
	org := strings.Split(repo, "/")[0]
	for _, r := range repos {
		if r == repo || r == org {
			return true
		}
	}
	return false
}
<|endoftext|>


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

package handle

import (
	"errors"

	"github.com/googlecloudplatform/gcsfuse/fs/inode"
	"github.com/googlecloudplatform/gcsfuse/internal/gcsx"
	"golang.org/x/net/context"
)

type FileHandle struct {
	inode *inode.FileInode

	// A random reader configured to some (potentially previous) generation of
	// the object backing the inode, or nil.
	//
	// INVARIANT: If reader != nil, reader.CheckInvariants() doesn't panic.
	//
	// GUARDED_BY(inode)
	reader gcsx.RandomReader
}

func NewFileHandle(inode *inode.FileInode) (fh *FileHandle, err error) {
	err = errors.New("TODO")
	return
}

// Panic if any internal invariants are violated.
//
// LOCKS_REQUIRED(fh.inode)
func (fh *FileHandle) CheckInvariants() {
	// INVARIANT: If reader != nil, reader.CheckInvariants() doesn't panic.
	if fh.reader != nil {
		fh.reader.CheckInvariants()
	}
}

// Destroy any resources associated with the handle, which must not be used
// again.
func (fh *FileHandle) Destroy() {
	panic("TODO")
}

// Return the inode backing this handle.
func (fh *FileHandle) Inode() *inode.FileInode {
	panic("TODO")
}

// Equivalent to locking fh.Inode() and calling fh.Inode().Read, but may be
// more efficient.
//
// LOCKS_EXCLUDED(fh.inode)
func (fh *FileHandle) Read(
	ctx context.Context,
	dst []byte,
	offset int64) (n int, err error) {
	err = errors.New("TODO")
	return
}
<commit_msg>FileHandle.Destroy<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package handle

import (
	"errors"

	"github.com/googlecloudplatform/gcsfuse/fs/inode"
	"github.com/googlecloudplatform/gcsfuse/internal/gcsx"
	"golang.org/x/net/context"
)

type FileHandle struct {
	inode *inode.FileInode

	// A random reader configured to some (potentially previous) generation of
	// the object backing the inode, or nil.
	//
	// INVARIANT: If reader != nil, reader.CheckInvariants() doesn't panic.
	//
	// GUARDED_BY(inode)
	reader gcsx.RandomReader
}

func NewFileHandle(inode *inode.FileInode) (fh *FileHandle, err error) {
	err = errors.New("TODO")
	return
}

// Panic if any internal invariants are violated.
//
// LOCKS_REQUIRED(fh.inode)
func (fh *FileHandle) CheckInvariants() {
	// INVARIANT: If reader != nil, reader.CheckInvariants() doesn't panic.
	if fh.reader != nil {
		fh.reader.CheckInvariants()
	}
}

// Destroy any resources associated with the handle, which must not be used
// again.
func (fh *FileHandle) Destroy() {
	if fh.reader != nil {
		fh.reader.Destroy()
	}
}

// Return the inode backing this handle.
func (fh *FileHandle) Inode() *inode.FileInode {
	panic("TODO")
}

// Equivalent to locking fh.Inode() and calling fh.Inode().Read, but may be
// more efficient.
//
// LOCKS_EXCLUDED(fh.inode)
func (fh *FileHandle) Read(
	ctx context.Context,
	dst []byte,
	offset int64) (n int, err error) {
	err = errors.New("TODO")
	return
}
<|endoftext|>


<commit_before>package k8s

import (
	"errors"
	"fmt"
	"sort"

	"github.com/nginxinc/kubernetes-ingress/internal/configs"
	networking "k8s.io/api/networking/v1beta1"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apimachinery/pkg/util/validation/field"
)

const (
	mergeableIngressTypeAnnotation       = "nginx.org/mergeable-ingress-type"
	lbMethodAnnotation                   = "nginx.org/lb-method"
	healthChecksAnnotation               = "nginx.com/health-checks"
	healthChecksMandatoryAnnotation      = "nginx.com/health-checks-mandatory"
	healthChecksMandatoryQueueAnnotation = "nginx.com/health-checks-mandatory-queue"
)

type annotationValidationContext struct {
	annotations map[string]string
	name        string
	value       string
	isPlus      bool
	fieldPath   *field.Path
}

type annotationValidationFunc func(context *annotationValidationContext) field.ErrorList
type validatorFunc func(val string) error

var (
	// annotationValidations defines the various validations which will be applied in order to each ingress annotation.
	// If any specified validation fails, the remaining validations for that annotation will not be run.
	annotationValidations = map[string][]annotationValidationFunc{
		mergeableIngressTypeAnnotation: {
			validateRequiredAnnotation,
			validateMergeableIngressTypeAnnotation,
		},
		lbMethodAnnotation: {
			validateRequiredAnnotation,
			validateLBMethodAnnotation,
		},
		healthChecksAnnotation: {
			validateRequiredAnnotation,
			validatePlusOnlyAnnotation,
			validateBoolAnnotation,
		},
		healthChecksMandatoryAnnotation: {
			validateRelatedAnnotation(healthChecksAnnotation, validateIsTrue),
			validateRequiredAnnotation,
			validateBoolAnnotation,
		},
		healthChecksMandatoryQueueAnnotation: {
			validateRelatedAnnotation(healthChecksMandatoryAnnotation, validateIsTrue),
			validateRequiredAnnotation,
			validateNonNegativeIntAnnotation,
		},
	}
	annotationNames = sortedAnnotationNames(annotationValidations)
)

func sortedAnnotationNames(annotationValidations map[string][]annotationValidationFunc) []string {
	sortedNames := make([]string, 0)
	for annotationName := range annotationValidations {
		sortedNames = append(sortedNames, annotationName)
	}
	sort.Strings(sortedNames)
	return sortedNames
}

// validateIngress validate an Ingress resource with rules that our Ingress Controller enforces.
// Note that the full validation of Ingress resources is done by Kubernetes.
func validateIngress(ing *networking.Ingress, isPlus bool) field.ErrorList {
	allErrs := field.ErrorList{}

	allErrs = append(allErrs, validateIngressAnnotations(ing.Annotations, isPlus, field.NewPath("annotations"))...)

	allErrs = append(allErrs, validateIngressSpec(&ing.Spec, field.NewPath("spec"))...)

	if isMaster(ing) {
		allErrs = append(allErrs, validateMasterSpec(&ing.Spec, field.NewPath("spec"))...)
	} else if isMinion(ing) {
		allErrs = append(allErrs, validateMinionSpec(&ing.Spec, field.NewPath("spec"))...)
	}

	return allErrs
}

func validateIngressAnnotations(annotations map[string]string, isPlus bool, fieldPath *field.Path) field.ErrorList {
	allErrs := field.ErrorList{}

	for _, name := range annotationNames {
		if value, nameExists := annotations[name]; nameExists {
			if validationFuncs, validationExists := annotationValidations[name]; validationExists {
				for _, validationFunc := range validationFuncs {
					valErrors := validationFunc(&annotationValidationContext{
						annotations: annotations,
						name:        name,
						value:       value,
						isPlus:      isPlus,
						fieldPath:   fieldPath.Child(name),
					})
					if len(valErrors) > 0 {
						allErrs = append(allErrs, valErrors...)
						break
					}
				}
			}
		}
	}

	return allErrs
}

func validateRelatedAnnotation(name string, validator validatorFunc) annotationValidationFunc {
	return func(context *annotationValidationContext) field.ErrorList {
		allErrs := field.ErrorList{}
		val, exists := context.annotations[name]
		if !exists {
			return append(allErrs, field.Forbidden(context.fieldPath, fmt.Sprintf("related annotation %s: must be set", name)))
		}

		if err := validator(val); err != nil {
			return append(allErrs, field.Forbidden(context.fieldPath, fmt.Sprintf("related annotation %s: %s", name, err.Error())))
		}
		return allErrs
	}
}

func validateMergeableIngressTypeAnnotation(context *annotationValidationContext) field.ErrorList {
	allErrs := field.ErrorList{}
	if context.value != "master" && context.value != "minion" {
		return append(allErrs, field.Invalid(context.fieldPath, context.value, "must be one of: 'master' or 'minion'"))
	}
	return allErrs
}

func validateLBMethodAnnotation(context *annotationValidationContext) field.ErrorList {
	allErrs := field.ErrorList{}
	if context.isPlus {
		if _, err := configs.ParseLBMethodForPlus(context.value); err != nil {
			return append(allErrs, field.Invalid(context.fieldPath, context.value, err.Error()))
		}
	} else {
		if _, err := configs.ParseLBMethod(context.value); err != nil {
			return append(allErrs, field.Invalid(context.fieldPath, context.value, err.Error()))
		}
	}
	return allErrs
}

func validateRequiredAnnotation(context *annotationValidationContext) field.ErrorList {
	allErrs := field.ErrorList{}
	if context.value == "" {
		return append(allErrs, field.Required(context.fieldPath, ""))
	}
	return allErrs
}

func validatePlusOnlyAnnotation(context *annotationValidationContext) field.ErrorList {
	allErrs := field.ErrorList{}
	if !context.isPlus {
		return append(allErrs, field.Forbidden(context.fieldPath, "annotation requires NGINX Plus"))
	}
	return allErrs
}

func validateBoolAnnotation(context *annotationValidationContext) field.ErrorList {
	allErrs := field.ErrorList{}
	if _, err := configs.ParseBool(context.value); err != nil {
		return append(allErrs, field.Invalid(context.fieldPath, context.value, "must be a valid boolean"))
	}
	return allErrs
}

func validateNonNegativeIntAnnotation(context *annotationValidationContext) field.ErrorList {
	allErrs := field.ErrorList{}
	if _, err := configs.ParseUint64(context.value); err != nil {
		return append(allErrs, field.Invalid(context.fieldPath, context.value, "must be a non-negative integer"))
	}
	return allErrs
}

func validateIsTrue(v string) error {
	b, err := configs.ParseBool(v)
	if err != nil {
		return err
	}
	if !b {
		return errors.New("must be true")
	}
	return nil
}

func validateIngressSpec(spec *networking.IngressSpec, fieldPath *field.Path) field.ErrorList {
	allErrs := field.ErrorList{}

	allHosts := sets.String{}

	if len(spec.Rules) == 0 {
		return append(allErrs, field.Required(fieldPath.Child("rules"), ""))
	}

	for i, r := range spec.Rules {
		idxPath := fieldPath.Child("rules").Index(i)

		if r.Host == "" {
			allErrs = append(allErrs, field.Required(idxPath.Child("host"), ""))
		} else if allHosts.Has(r.Host) {
			allErrs = append(allErrs, field.Duplicate(idxPath.Child("host"), r.Host))
		} else {
			allHosts.Insert(r.Host)
		}
	}

	return allErrs
}

func validateMasterSpec(spec *networking.IngressSpec, fieldPath *field.Path) field.ErrorList {
	allErrs := field.ErrorList{}

	if len(spec.Rules) != 1 {
		return append(allErrs, field.TooMany(fieldPath.Child("rules"), len(spec.Rules), 1))
	}

	// the number of paths of the first rule of the spec must be 0
	if spec.Rules[0].HTTP != nil && len(spec.Rules[0].HTTP.Paths) > 0 {
		pathsField := fieldPath.Child("rules").Index(0).Child("http").Child("paths")
		return append(allErrs, field.TooMany(pathsField, len(spec.Rules[0].HTTP.Paths), 0))
	}

	return allErrs
}

func validateMinionSpec(spec *networking.IngressSpec, fieldPath *field.Path) field.ErrorList {
	allErrs := field.ErrorList{}

	if len(spec.TLS) > 0 {
		allErrs = append(allErrs, field.TooMany(fieldPath.Child("tls"), len(spec.TLS), 0))
	}

	if len(spec.Rules) != 1 {
		return append(allErrs, field.TooMany(fieldPath.Child("rules"), len(spec.Rules), 1))
	}

	// the number of paths of the first rule of the spec must be greater than 0
	if spec.Rules[0].HTTP == nil || len(spec.Rules[0].HTTP.Paths) == 0 {
		pathsField := fieldPath.Child("rules").Index(0).Child("http").Child("paths")
		return append(allErrs, field.Required(pathsField, "must include at least one path"))
	}

	return allErrs
}
<commit_msg>Refactor validateIngressAnnotations func<commit_after>package k8s

import (
	"errors"
	"fmt"
	"sort"

	"github.com/nginxinc/kubernetes-ingress/internal/configs"
	networking "k8s.io/api/networking/v1beta1"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apimachinery/pkg/util/validation/field"
)

const (
	mergeableIngressTypeAnnotation       = "nginx.org/mergeable-ingress-type"
	lbMethodAnnotation                   = "nginx.org/lb-method"
	healthChecksAnnotation               = "nginx.com/health-checks"
	healthChecksMandatoryAnnotation      = "nginx.com/health-checks-mandatory"
	healthChecksMandatoryQueueAnnotation = "nginx.com/health-checks-mandatory-queue"
)

type annotationValidationContext struct {
	annotations map[string]string
	name        string
	value       string
	isPlus      bool
	fieldPath   *field.Path
}

type annotationValidationFunc func(context *annotationValidationContext) field.ErrorList
type validatorFunc func(val string) error

var (
	// annotationValidations defines the various validations which will be applied in order to each ingress annotation.
	// If any specified validation fails, the remaining validations for that annotation will not be run.
	annotationValidations = map[string][]annotationValidationFunc{
		mergeableIngressTypeAnnotation: {
			validateRequiredAnnotation,
			validateMergeableIngressTypeAnnotation,
		},
		lbMethodAnnotation: {
			validateRequiredAnnotation,
			validateLBMethodAnnotation,
		},
		healthChecksAnnotation: {
			validateRequiredAnnotation,
			validatePlusOnlyAnnotation,
			validateBoolAnnotation,
		},
		healthChecksMandatoryAnnotation: {
			validateRelatedAnnotation(healthChecksAnnotation, validateIsTrue),
			validateRequiredAnnotation,
			validateBoolAnnotation,
		},
		healthChecksMandatoryQueueAnnotation: {
			validateRelatedAnnotation(healthChecksMandatoryAnnotation, validateIsTrue),
			validateRequiredAnnotation,
			validateNonNegativeIntAnnotation,
		},
	}
	annotationNames = sortedAnnotationNames(annotationValidations)
)

func sortedAnnotationNames(annotationValidations map[string][]annotationValidationFunc) []string {
	sortedNames := make([]string, 0)
	for annotationName := range annotationValidations {
		sortedNames = append(sortedNames, annotationName)
	}
	sort.Strings(sortedNames)
	return sortedNames
}

// validateIngress validate an Ingress resource with rules that our Ingress Controller enforces.
// Note that the full validation of Ingress resources is done by Kubernetes.
func validateIngress(ing *networking.Ingress, isPlus bool) field.ErrorList {
	allErrs := field.ErrorList{}

	allErrs = append(allErrs, validateIngressAnnotations(ing.Annotations, isPlus, field.NewPath("annotations"))...)

	allErrs = append(allErrs, validateIngressSpec(&ing.Spec, field.NewPath("spec"))...)

	if isMaster(ing) {
		allErrs = append(allErrs, validateMasterSpec(&ing.Spec, field.NewPath("spec"))...)
	} else if isMinion(ing) {
		allErrs = append(allErrs, validateMinionSpec(&ing.Spec, field.NewPath("spec"))...)
	}

	return allErrs
}

func validateIngressAnnotations(annotations map[string]string, isPlus bool, fieldPath *field.Path) field.ErrorList {
	allErrs := field.ErrorList{}
	for _, name := range annotationNames {
		if value, exists := annotations[name]; exists {
			allErrs = append(allErrs, validateIngressAnnotation(&annotationValidationContext{
				annotations: annotations,
				name:        name,
				value:       value,
				isPlus:      isPlus,
				fieldPath:   fieldPath.Child(name),
			})...)
		}
	}
	return allErrs
}

func validateIngressAnnotation(context *annotationValidationContext) field.ErrorList {
	allErrs := field.ErrorList{}
	if validationFuncs, exists := annotationValidations[context.name]; exists {
		for _, validationFunc := range validationFuncs {
			valErrors := validationFunc(context)
			if len(valErrors) > 0 {
				allErrs = append(allErrs, valErrors...)
				break
			}
		}
	}
	return allErrs
}

func validateRelatedAnnotation(name string, validator validatorFunc) annotationValidationFunc {
	return func(context *annotationValidationContext) field.ErrorList {
		allErrs := field.ErrorList{}
		val, exists := context.annotations[name]
		if !exists {
			return append(allErrs, field.Forbidden(context.fieldPath, fmt.Sprintf("related annotation %s: must be set", name)))
		}

		if err := validator(val); err != nil {
			return append(allErrs, field.Forbidden(context.fieldPath, fmt.Sprintf("related annotation %s: %s", name, err.Error())))
		}
		return allErrs
	}
}

func validateMergeableIngressTypeAnnotation(context *annotationValidationContext) field.ErrorList {
	allErrs := field.ErrorList{}
	if context.value != "master" && context.value != "minion" {
		return append(allErrs, field.Invalid(context.fieldPath, context.value, "must be one of: 'master' or 'minion'"))
	}
	return allErrs
}

func validateLBMethodAnnotation(context *annotationValidationContext) field.ErrorList {
	allErrs := field.ErrorList{}
	if context.isPlus {
		if _, err := configs.ParseLBMethodForPlus(context.value); err != nil {
			return append(allErrs, field.Invalid(context.fieldPath, context.value, err.Error()))
		}
	} else {
		if _, err := configs.ParseLBMethod(context.value); err != nil {
			return append(allErrs, field.Invalid(context.fieldPath, context.value, err.Error()))
		}
	}
	return allErrs
}

func validateRequiredAnnotation(context *annotationValidationContext) field.ErrorList {
	allErrs := field.ErrorList{}
	if context.value == "" {
		return append(allErrs, field.Required(context.fieldPath, ""))
	}
	return allErrs
}

func validatePlusOnlyAnnotation(context *annotationValidationContext) field.ErrorList {
	allErrs := field.ErrorList{}
	if !context.isPlus {
		return append(allErrs, field.Forbidden(context.fieldPath, "annotation requires NGINX Plus"))
	}
	return allErrs
}

func validateBoolAnnotation(context *annotationValidationContext) field.ErrorList {
	allErrs := field.ErrorList{}
	if _, err := configs.ParseBool(context.value); err != nil {
		return append(allErrs, field.Invalid(context.fieldPath, context.value, "must be a valid boolean"))
	}
	return allErrs
}

func validateNonNegativeIntAnnotation(context *annotationValidationContext) field.ErrorList {
	allErrs := field.ErrorList{}
	if _, err := configs.ParseUint64(context.value); err != nil {
		return append(allErrs, field.Invalid(context.fieldPath, context.value, "must be a non-negative integer"))
	}
	return allErrs
}

func validateIsTrue(v string) error {
	b, err := configs.ParseBool(v)
	if err != nil {
		return err
	}
	if !b {
		return errors.New("must be true")
	}
	return nil
}

func validateIngressSpec(spec *networking.IngressSpec, fieldPath *field.Path) field.ErrorList {
	allErrs := field.ErrorList{}

	allHosts := sets.String{}

	if len(spec.Rules) == 0 {
		return append(allErrs, field.Required(fieldPath.Child("rules"), ""))
	}

	for i, r := range spec.Rules {
		idxPath := fieldPath.Child("rules").Index(i)

		if r.Host == "" {
			allErrs = append(allErrs, field.Required(idxPath.Child("host"), ""))
		} else if allHosts.Has(r.Host) {
			allErrs = append(allErrs, field.Duplicate(idxPath.Child("host"), r.Host))
		} else {
			allHosts.Insert(r.Host)
		}
	}

	return allErrs
}

func validateMasterSpec(spec *networking.IngressSpec, fieldPath *field.Path) field.ErrorList {
	allErrs := field.ErrorList{}

	if len(spec.Rules) != 1 {
		return append(allErrs, field.TooMany(fieldPath.Child("rules"), len(spec.Rules), 1))
	}

	// the number of paths of the first rule of the spec must be 0
	if spec.Rules[0].HTTP != nil && len(spec.Rules[0].HTTP.Paths) > 0 {
		pathsField := fieldPath.Child("rules").Index(0).Child("http").Child("paths")
		return append(allErrs, field.TooMany(pathsField, len(spec.Rules[0].HTTP.Paths), 0))
	}

	return allErrs
}

func validateMinionSpec(spec *networking.IngressSpec, fieldPath *field.Path) field.ErrorList {
	allErrs := field.ErrorList{}

	if len(spec.TLS) > 0 {
		allErrs = append(allErrs, field.TooMany(fieldPath.Child("tls"), len(spec.TLS), 0))
	}

	if len(spec.Rules) != 1 {
		return append(allErrs, field.TooMany(fieldPath.Child("rules"), len(spec.Rules), 1))
	}

	// the number of paths of the first rule of the spec must be greater than 0
	if spec.Rules[0].HTTP == nil || len(spec.Rules[0].HTTP.Paths) == 0 {
		pathsField := fieldPath.Child("rules").Index(0).Child("http").Child("paths")
		return append(allErrs, field.Required(pathsField, "must include at least one path"))
	}

	return allErrs
}
<|endoftext|>


<commit_before>package snmp

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/influxdata/telegraf"
	"github.com/sleepinggenius2/gosmi"
	"github.com/sleepinggenius2/gosmi/types"
)

// must init, append path for each directory, load module for every file
// or gosmi will fail without saying why
var m sync.Mutex
var once sync.Once
var cache = make(map[string]bool)

type MibLoader interface {
	loadModule(path string) error
	appendPath(path string)
}

type GosmiMibLoader struct{}

func (*GosmiMibLoader) appendPath(path string) {
	m.Lock()
	defer m.Unlock()

	gosmi.AppendPath(path)
}

func (*GosmiMibLoader) loadModule(path string) error {
	m.Lock()
	defer m.Unlock()

	_, err := gosmi.LoadModule(path)
	return err
}

func ClearCache() {
	cache = make(map[string]bool)
}

//will give all found folders to gosmi and load in all modules found in the folders
func LoadMibsFromPath(paths []string, log telegraf.Logger, loader MibLoader) error {
	folders, err := walkPaths(paths, log)
	if err != nil {
		return err
	}
	for _, path := range folders {
		loader.appendPath(path)
		modules, err := ioutil.ReadDir(path)
		if err != nil {
			log.Warnf("Can't read directory %v", modules)
		}

		for _, info := range modules {
			if info.Mode()&os.ModeSymlink != 0 {
				target, err := filepath.EvalSymlinks(path)
				if err != nil {
					log.Warnf("Bad symbolic link %v", target)
					continue
				}
				info, err = os.Lstat(filepath.Join(path, target))
				if err != nil {
					log.Warnf("Couldn't stat target %v", target)
					continue
				}
				path = target
			}
			if info.Mode().IsRegular() {
				err := loader.loadModule(info.Name())
				if err != nil {
					log.Warnf("module %v could not be loaded", info.Name())
					continue
				}
			}
		}
	}
	return nil
}

//should walk the paths given and find all folders
func walkPaths(paths []string, log telegraf.Logger) ([]string, error) {
	once.Do(gosmi.Init)
	folders := []string{}

	for _, mibPath := range paths {
		// Check if we loaded that path already and skip it if so
		m.Lock()
		cached := cache[mibPath]
		cache[mibPath] = true
		m.Unlock()
		if cached {
			continue
		}

		err := filepath.Walk(mibPath, func(path string, info os.FileInfo, err error) error {
			if info == nil {
				log.Warnf("No mibs found")
				if os.IsNotExist(err) {
					log.Warnf("MIB path doesn't exist: %q", mibPath)
				} else if err != nil {
					return err
				}
				return nil
			}

			if info.Mode()&os.ModeSymlink != 0 {
				target, err := filepath.EvalSymlinks(path)
				if err != nil {
					log.Warnf("Could not evaluate link %v", target)
				}
				info, err = os.Lstat(target)
				if err != nil {
					log.Warnf("Couldn't stat target %v", path)
				}
				path = target
			}
			if info.IsDir() {
				folders = append(folders, path)
			}

			return nil
		})
		if err != nil {
			return folders, fmt.Errorf("Filepath %q could not be walked: %v", mibPath, err)
		}
	}
	return folders, nil
}

// The following is for snmp_trap
type MibEntry struct {
	MibName string
	OidText string
}

func TrapLookup(oid string) (e MibEntry, err error) {
	var givenOid types.Oid
	if givenOid, err = types.OidFromString(oid); err != nil {
		return e, fmt.Errorf("could not convert OID %s: %w", oid, err)
	}

	// Get node name
	var node gosmi.SmiNode
	if node, err = gosmi.GetNodeByOID(givenOid); err != nil {
		return e, err
	}
	e.OidText = node.Name

	// Add not found OID part
	if !givenOid.Equals(node.Oid) {
		e.OidText += "." + givenOid[len(node.Oid):].String()
	}

	// Get module name
	module := node.GetModule()
	if module.Name != "<well-known>" {
		e.MibName = module.Name
	}

	return e, nil
}

// The following is for snmp

func GetIndex(oidNum string, mibPrefix string, node gosmi.SmiNode) (col []string, tagOids map[string]struct{}, err error) {
	// first attempt to get the table's tags
	tagOids = map[string]struct{}{}

	// mimcks grabbing INDEX {} that is returned from snmptranslate -Td MibName
	for _, index := range node.GetIndex() {
		//nolint:staticcheck //assaignment to nil map to keep backwards compatibilty
		tagOids[mibPrefix+index.Name] = struct{}{}
	}

	// grabs all columns from the table
	// mimmicks grabbing everything returned from snmptable -Ch -Cl -c public 127.0.0.1 oidFullName
	_, col = node.GetColumns()

	return col, tagOids, nil
}

//nolint:revive //Too many return variable but necessary
func SnmpTranslateCall(oid string) (mibName string, oidNum string, oidText string, conversion string, node gosmi.SmiNode, err error) {
	var out gosmi.SmiNode
	var end string
	if strings.ContainsAny(oid, "::") {
		// split given oid
		// for example RFC1213-MIB::sysUpTime.0
		s := strings.SplitN(oid, "::", 2)
		// moduleName becomes RFC1213
		moduleName := s[0]
		module, err := gosmi.GetModule(moduleName)
		if err != nil {
			return oid, oid, oid, oid, gosmi.SmiNode{}, err
		}
		if s[1] == "" {
			return "", oid, oid, oid, gosmi.SmiNode{}, fmt.Errorf("cannot parse %v\n", oid)
		}
		// node becomes sysUpTime.0
		node := s[1]
		if strings.ContainsAny(node, ".") {
			s = strings.SplitN(node, ".", 2)
			// node becomes sysUpTime
			node = s[0]
			end = "." + s[1]
		}

		out, err = module.GetNode(node)
		if err != nil {
			return oid, oid, oid, oid, out, err
		}

		if oidNum = out.RenderNumeric(); oidNum == "" {
			return oid, oid, oid, oid, out, fmt.Errorf("cannot make %v numeric, please ensure all imported mibs are in the path", oid)
		}

		oidNum = "." + oidNum + end
	} else if strings.ContainsAny(oid, "abcdefghijklnmopqrstuvwxyz") {
		//handle mixed oid ex. .iso.2.3
		s := strings.Split(oid, ".")
		for i := range s {
			if strings.ContainsAny(s[i], "abcdefghijklmnopqrstuvwxyz") {
				out, err = gosmi.GetNode(s[i])
				if err != nil {
					return oid, oid, oid, oid, out, err
				}
				s[i] = out.RenderNumeric()
			}
		}
		oidNum = strings.Join(s, ".")
		out, _ = gosmi.GetNodeByOID(types.OidMustFromString(oidNum))
	} else {
		out, err = gosmi.GetNodeByOID(types.OidMustFromString(oid))
		oidNum = oid
		// ensure modules are loaded or node will be empty (might not error)
		// do not return the err as the oid is numeric and telegraf can continue
		//nolint:nilerr
		if err != nil || out.Name == "iso" {
			return oid, oid, oid, oid, out, nil
		}
	}

	tc := out.GetSubtree()

	for i := range tc {
		// case where the mib doesn't have a conversion so Type struct will be nil
		// prevents seg fault
		if tc[i].Type == nil {
			break
		}
		switch tc[i].Type.Name {
		case "MacAddress", "PhysAddress":
			conversion = "hwaddr"
		case "InetAddressIPv4", "InetAddressIPv6", "InetAddress", "IPSIpAddress":
			conversion = "ipaddr"
		}
	}

	oidText = out.RenderQualified()
	i := strings.Index(oidText, "::")
	if i == -1 {
		return "", oid, oid, oid, out, fmt.Errorf("not found")
	}
	mibName = oidText[:i]
	oidText = oidText[i+2:] + end

	return mibName, oidNum, oidText, conversion, out, nil
}
<commit_msg>fix: log err when loading mibs (#10735)<commit_after>package snmp

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/influxdata/telegraf"
	"github.com/sleepinggenius2/gosmi"
	"github.com/sleepinggenius2/gosmi/types"
)

// must init, append path for each directory, load module for every file
// or gosmi will fail without saying why
var m sync.Mutex
var once sync.Once
var cache = make(map[string]bool)

type MibLoader interface {
	loadModule(path string) error
	appendPath(path string)
}

type GosmiMibLoader struct{}

func (*GosmiMibLoader) appendPath(path string) {
	m.Lock()
	defer m.Unlock()

	gosmi.AppendPath(path)
}

func (*GosmiMibLoader) loadModule(path string) error {
	m.Lock()
	defer m.Unlock()

	_, err := gosmi.LoadModule(path)
	return err
}

func ClearCache() {
	cache = make(map[string]bool)
}

//will give all found folders to gosmi and load in all modules found in the folders
func LoadMibsFromPath(paths []string, log telegraf.Logger, loader MibLoader) error {
	folders, err := walkPaths(paths, log)
	if err != nil {
		return err
	}
	for _, path := range folders {
		loader.appendPath(path)
		modules, err := ioutil.ReadDir(path)
		if err != nil {
			log.Warnf("Can't read directory %v", modules)
		}

		for _, info := range modules {
			if info.Mode()&os.ModeSymlink != 0 {
				target, err := filepath.EvalSymlinks(path)
				if err != nil {
					log.Warnf("Couldn't evaluate symbolic links for %v: %v", target, err)
					continue
				}
				info, err = os.Lstat(filepath.Join(path, target))
				if err != nil {
					log.Warnf("Couldn't stat target %v: %v", target, err)
					continue
				}
				path = target
			}
			if info.Mode().IsRegular() {
				err := loader.loadModule(info.Name())
				if err != nil {
					log.Warnf("Couldn't load module %v: %v", info.Name(), err)
					continue
				}
			}
		}
	}
	return nil
}

//should walk the paths given and find all folders
func walkPaths(paths []string, log telegraf.Logger) ([]string, error) {
	once.Do(gosmi.Init)
	folders := []string{}

	for _, mibPath := range paths {
		// Check if we loaded that path already and skip it if so
		m.Lock()
		cached := cache[mibPath]
		cache[mibPath] = true
		m.Unlock()
		if cached {
			continue
		}

		err := filepath.Walk(mibPath, func(path string, info os.FileInfo, err error) error {
			if info == nil {
				log.Warnf("No mibs found")
				if os.IsNotExist(err) {
					log.Warnf("MIB path doesn't exist: %q", mibPath)
				} else if err != nil {
					return err
				}
				return nil
			}

			if info.Mode()&os.ModeSymlink != 0 {
				target, err := filepath.EvalSymlinks(path)
				if err != nil {
					log.Warnf("Couldn't evaluate symbolic links for %v: %v", target, err)
				}
				info, err = os.Lstat(target)
				if err != nil {
					log.Warnf("Couldn't stat target %v: %v", target, err)
				}
				path = target
			}
			if info.IsDir() {
				folders = append(folders, path)
			}

			return nil
		})
		if err != nil {
			return folders, fmt.Errorf("Couldn't walk path %q: %v", mibPath, err)
		}
	}
	return folders, nil
}

// The following is for snmp_trap
type MibEntry struct {
	MibName string
	OidText string
}

func TrapLookup(oid string) (e MibEntry, err error) {
	var givenOid types.Oid
	if givenOid, err = types.OidFromString(oid); err != nil {
		return e, fmt.Errorf("could not convert OID %s: %w", oid, err)
	}

	// Get node name
	var node gosmi.SmiNode
	if node, err = gosmi.GetNodeByOID(givenOid); err != nil {
		return e, err
	}
	e.OidText = node.Name

	// Add not found OID part
	if !givenOid.Equals(node.Oid) {
		e.OidText += "." + givenOid[len(node.Oid):].String()
	}

	// Get module name
	module := node.GetModule()
	if module.Name != "<well-known>" {
		e.MibName = module.Name
	}

	return e, nil
}

// The following is for snmp

func GetIndex(oidNum string, mibPrefix string, node gosmi.SmiNode) (col []string, tagOids map[string]struct{}, err error) {
	// first attempt to get the table's tags
	tagOids = map[string]struct{}{}

	// mimcks grabbing INDEX {} that is returned from snmptranslate -Td MibName
	for _, index := range node.GetIndex() {
		//nolint:staticcheck //assaignment to nil map to keep backwards compatibilty
		tagOids[mibPrefix+index.Name] = struct{}{}
	}

	// grabs all columns from the table
	// mimmicks grabbing everything returned from snmptable -Ch -Cl -c public 127.0.0.1 oidFullName
	_, col = node.GetColumns()

	return col, tagOids, nil
}

//nolint:revive //Too many return variable but necessary
func SnmpTranslateCall(oid string) (mibName string, oidNum string, oidText string, conversion string, node gosmi.SmiNode, err error) {
	var out gosmi.SmiNode
	var end string
	if strings.ContainsAny(oid, "::") {
		// split given oid
		// for example RFC1213-MIB::sysUpTime.0
		s := strings.SplitN(oid, "::", 2)
		// moduleName becomes RFC1213
		moduleName := s[0]
		module, err := gosmi.GetModule(moduleName)
		if err != nil {
			return oid, oid, oid, oid, gosmi.SmiNode{}, err
		}
		if s[1] == "" {
			return "", oid, oid, oid, gosmi.SmiNode{}, fmt.Errorf("cannot parse %v\n", oid)
		}
		// node becomes sysUpTime.0
		node := s[1]
		if strings.ContainsAny(node, ".") {
			s = strings.SplitN(node, ".", 2)
			// node becomes sysUpTime
			node = s[0]
			end = "." + s[1]
		}

		out, err = module.GetNode(node)
		if err != nil {
			return oid, oid, oid, oid, out, err
		}

		if oidNum = out.RenderNumeric(); oidNum == "" {
			return oid, oid, oid, oid, out, fmt.Errorf("cannot make %v numeric, please ensure all imported mibs are in the path", oid)
		}

		oidNum = "." + oidNum + end
	} else if strings.ContainsAny(oid, "abcdefghijklnmopqrstuvwxyz") {
		//handle mixed oid ex. .iso.2.3
		s := strings.Split(oid, ".")
		for i := range s {
			if strings.ContainsAny(s[i], "abcdefghijklmnopqrstuvwxyz") {
				out, err = gosmi.GetNode(s[i])
				if err != nil {
					return oid, oid, oid, oid, out, err
				}
				s[i] = out.RenderNumeric()
			}
		}
		oidNum = strings.Join(s, ".")
		out, _ = gosmi.GetNodeByOID(types.OidMustFromString(oidNum))
	} else {
		out, err = gosmi.GetNodeByOID(types.OidMustFromString(oid))
		oidNum = oid
		// ensure modules are loaded or node will be empty (might not error)
		// do not return the err as the oid is numeric and telegraf can continue
		//nolint:nilerr
		if err != nil || out.Name == "iso" {
			return oid, oid, oid, oid, out, nil
		}
	}

	tc := out.GetSubtree()

	for i := range tc {
		// case where the mib doesn't have a conversion so Type struct will be nil
		// prevents seg fault
		if tc[i].Type == nil {
			break
		}
		switch tc[i].Type.Name {
		case "MacAddress", "PhysAddress":
			conversion = "hwaddr"
		case "InetAddressIPv4", "InetAddressIPv6", "InetAddress", "IPSIpAddress":
			conversion = "ipaddr"
		}
	}

	oidText = out.RenderQualified()
	i := strings.Index(oidText, "::")
	if i == -1 {
		return "", oid, oid, oid, out, fmt.Errorf("not found")
	}
	mibName = oidText[:i]
	oidText = oidText[i+2:] + end

	return mibName, oidNum, oidText, conversion, out, nil
}
<|endoftext|>


<commit_before>package franklinreiter

import (
	"fmt"
	"log"

	"github.com/sourcekris/goRsaTool/keys"
	"github.com/sourcekris/goRsaTool/ln"

	fmp "github.com/sourcekris/goflint"
)

// name is the name of this attack.
const name = "franklin reiter related message attack"

// Attack implements the franklin reiter related message attack against two keys.
func Attack(ks []*keys.RSA) error {
	if len(ks) != 2 {
		return fmt.Errorf("%s requires exactly 2 keys to work - got %d", name, len(ks))
	}

	if ks[0].KnownPlainText == nil || ks[1].KnownPlainText == nil {
		return fmt.Errorf("%s requires each key has a corresponding known plaintext component", name)
	}

	if ks[0].CipherText == nil || ks[1].CipherText == nil {
		return fmt.Errorf("%s requires each key has a corresponding ciphertext", name)
	}

	c1 := ln.BytesToNumber(ks[0].CipherText)
	c2 := ln.BytesToNumber(ks[1].CipherText)
	s1 := ln.BytesToNumber(ks[0].KnownPlainText)
	s2 := ln.BytesToNumber(ks[1].KnownPlainText)
	e := ks[0].Key.PublicKey.E.GetInt()
	n := ks[0].Key.N

	// f = (x-s1+s2)^e - c1
	f := fmp.NewFmpzModPoly(n).SetCoeffUI(1, 1)
	f.Sub(f, fmp.NewFmpzModPoly(n).SetCoeff(0, s2)).Add(f, fmp.NewFmpzModPoly(n).SetCoeff(0, s1)).Pow(f, e)
	f.Sub(f, fmp.NewFmpzModPoly(n).SetCoeff(0, c1))

	// g = x^e-c2
	g := fmp.NewFmpzModPoly(n).SetCoeffUI(1, 1)
	g.Pow(g, e).Sub(g, fmp.NewFmpzModPoly(n).SetCoeff(0, c2))

	a := fmp.NewFmpzModPoly(f.GetMod()).Set(f)
	b := fmp.NewFmpzModPoly(g.GetMod()).Set(g)

	zero := fmp.NewFmpzModPoly(n).Zero()
	rp := fmp.NewFmpzModPoly(n)

	if ks[0].Verbose {
		log.Printf("%s beginning, this can sometimes crash, try it again if it does.", name)
	}

	var r *fmp.FmpzModPoly
	for {
		_, r = a.DivRem(b)

		if r.Equal(zero) {
			co0 := rp.GetCoeff(0)
			co1 := rp.GetCoeff(1)

			q, _ := fmp.NewFmpzModPoly(n).SetCoeff(0, ln.BigOne).DivRem(fmp.NewFmpzModPoly(n).SetCoeff(0, co1))
			q.MulScalar(q, ln.BigNOne).MulScalar(q, co0)
			ks[0].PlainText = ln.NumberToBytes(q.GetCoeff(0))
			return nil
		}

		rp.Set(r)
		a.Set(b)
		b.Set(r)
	}
}
<commit_msg>reorg signature attack module<commit_after>package franklinreiter

import (
	"fmt"
	"log"

	"github.com/sourcekris/goRsaTool/keys"
	"github.com/sourcekris/goRsaTool/ln"

	fmp "github.com/sourcekris/goflint"
)

// name is the name of this attack.
const name = "franklin reiter related message attack"

type sigAttack struct {
	cs []*fmp.Fmpz
	ss []*fmp.Fmpz
	n  *fmp.Fmpz
	e  int
}

// attempt runs the attack attempt itself.
func (s *sigAttack) attempt(v bool) []byte {
	// f = (x-s1+s2)^e - c1
	f := fmp.NewFmpzModPoly(s.n).SetCoeffUI(1, 1)
	f.Sub(f, fmp.NewFmpzModPoly(s.n).SetCoeff(0, s.ss[1])).Add(f, fmp.NewFmpzModPoly(s.n).SetCoeff(0, s.ss[0])).Pow(f, s.e)
	f.Sub(f, fmp.NewFmpzModPoly(s.n).SetCoeff(0, s.cs[0]))

	// g = x^e-c2
	g := fmp.NewFmpzModPoly(s.n).SetCoeffUI(1, 1)
	g.Pow(g, s.e).Sub(g, fmp.NewFmpzModPoly(s.n).SetCoeff(0, s.cs[1]))

	a := fmp.NewFmpzModPoly(f.GetMod()).Set(f)
	b := fmp.NewFmpzModPoly(g.GetMod()).Set(g)

	zero := fmp.NewFmpzModPoly(s.n).Zero()
	rp := fmp.NewFmpzModPoly(s.n)

	if v {
		log.Printf("%s beginning, this can sometimes crash, try it again if it does.", name)
	}

	var r *fmp.FmpzModPoly
	for {
		_, r = a.DivRem(b)

		if r.Equal(zero) {
			co0 := rp.GetCoeff(0)
			co1 := rp.GetCoeff(1)

			q, _ := fmp.NewFmpzModPoly(s.n).SetCoeff(0, ln.BigOne).DivRem(fmp.NewFmpzModPoly(s.n).SetCoeff(0, co1))
			q.MulScalar(q, ln.BigNOne).MulScalar(q, co0)
			return ln.NumberToBytes(q.GetCoeff(0))
		}

		rp.Set(r)
		a.Set(b)
		b.Set(r)
	}
}

// Attack implements the franklin reiter related message attack against two keys.
func Attack(ks []*keys.RSA) error {
	if len(ks) != 2 {
		return fmt.Errorf("%s requires exactly 2 keys to work - got %d", name, len(ks))
	}

	if ks[0].KnownPlainText == nil || ks[1].KnownPlainText == nil {
		return fmt.Errorf("%s requires each key has a corresponding known plaintext component", name)
	}

	if ks[0].CipherText == nil || ks[1].CipherText == nil {
		return fmt.Errorf("%s requires each key has a corresponding ciphertext", name)
	}

	sa := &sigAttack{n: ks[0].Key.N, e: ks[0].Key.PublicKey.E.GetInt()}
	for i := 0; i < 2; i++ {
		sa.ss = append(sa.ss, ln.BytesToNumber(ks[i].KnownPlainText))
		sa.cs = append(sa.cs, ln.BytesToNumber(ks[i].CipherText))
	}

	if res := sa.attempt(ks[0].Verbose); res != nil {
		ks[0].PlainText = res
		return nil
	}

	return fmt.Errorf("%s failed to recover the plaintext", name)
}
<|endoftext|>


<commit_before>package main

import (
    "bufio"
    "bytes"
    "encoding/json"
    "flag"
    "fmt"
    "io/ioutil"
    "log"
    "net"
    "net/http"
    "net/url"
    "os"
    "os/signal"
    "strings"
    "sync"
    "syscall"
    "time"
    "tokenbucket"
)

const _VERSION string = "0.2.0"

type Route struct {
    Bandwidth int64 `json:"bandwidth"`    // Bits per second
    Buffersize uint64 `json:"buffersize"` // Bytes
    Inspect bool `json:"inspect"`         // True = proxy, false = reverse proxy
    Src string `json:"src"`
    Dst []string `json:"dst"`
}

type Itinerary struct {
    Map map[string]Route
}

func sigHandler(ch *chan os.Signal) {
    sig := <-*ch
    fmt.Println("Captured sig", sig)
    os.Exit(3)
}

func main() {
    sigs := make(chan os.Signal, 1)
    signal.Notify(sigs,
                  syscall.SIGHUP,
                  syscall.SIGINT,
                  syscall.SIGQUIT,
                  syscall.SIGABRT,
                  syscall.SIGKILL,
                  syscall.SIGSEGV,
                  syscall.SIGTERM)

    log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)

    itineraryFile := flag.String("itinerary", "itinerary.json", "file containing source and destination routes")
    flag.Usage = func() {
        fmt.Fprintf(os.Stderr, "version %s\n", _VERSION)
        fmt.Fprintln(os.Stderr, "usage:")
        flag.PrintDefaults()
    }
    flag.Parse()

    itinerary := loadItinerary(itineraryFile)

    for _, m := range itinerary.Map {
        go intercept(m)
    }

    sigHandler(&sigs)
}

func loadItinerary(fileName *string) Itinerary {

    file, _ := os.Open(*fileName)
    decoder := json.NewDecoder(file)
    itinerary := Itinerary{}
    err := decoder.Decode(&itinerary)
    if err != nil {
        log.Println(err.Error())
    }

    return itinerary
}

func intercept(route Route) {
    listener, err := net.Listen("tcp", route.Src)
    if err == nil {
        log.Println("Listening on", route.Src)
        i := 0
        count := 0

        for {
            con, err := listener.Accept()
            if err == nil {
                count = count + 1

                if route.Inspect { // Proxy mode
                    go findHttpRoute(count, con, route.Bandwidth / 8, route.Buffersize)
                } else { // Load balancer mode
                    // Round-robin for now
                    go findRoute(count, con, route.Dst[i], route.Bandwidth / 8, route.Buffersize)
                    i = (i + 1) % len(route.Dst)
                }
            } else {
                log.Println(err.Error())
            }
        }
        listener.Close()
    } else {
        log.Println(err.Error())
    }
}

func parseHttpRequestUri(header *http.Request) string {
    var uri string
    if header.Method == "HEAD" || header.Method == "PUT" {
        url, err := url.Parse(header.RequestURI)

        if err == nil {
            if strings.ContainsAny(url.Host, ":") {
                uri = url.Host
            } else {
                switch url.Scheme {
                    case "http":
                        uri = url.Host + ":80"
                    case "https":
                        uri = url.Host + ":443"
                    default:
                        uri = url.Host
                }
            }
        } else {
            log.Println(err.Error())
        }
    } else if header.Method == "CONNECT" {
        uri = header.RequestURI
    }

    return uri
}

func findHttpRoute(id int, src net.Conn, bandwidth int64, bufferSize uint64) {
    buf := make([]byte, 65536)
    size, err := src.Read(buf) // Assume one read will include entire HTTP header

    if err == nil {
        reader := bufio.NewReader(strings.NewReader(string(buf[0:size])))
        header, _ := http.ReadRequest(reader)

        dstAddr := parseHttpRequestUri(header)
        dst, err := net.Dial("tcp", dstAddr)

        if err == nil && header.Method == "CONNECT" {
            body := ""
            rsp := &http.Response {
                Status:        "200 OK",
                StatusCode:    200,
                Proto:         "HTTP/1.1",
                ProtoMajor:    1,
                ProtoMinor:    1,
                Body:          ioutil.NopCloser(bytes.NewBufferString(body)),
                ContentLength: int64(len(body)),
                Request:       nil,
                Header:        make(http.Header, 0),
            }
            buff := bytes.NewBuffer(nil)
            rsp.Write(buff)
            src.Write(buff.Bytes())
            startDetour(id, src, dst, bandwidth, bufferSize)
        } else {
            src.Close()
            log.Println(err.Error())
        }
    } else {
        src.Close()
        log.Println(err.Error())
    }
}

func findRoute(id int, src net.Conn, dstAddr string, bandwidth int64, bufferSize uint64) {
    dst, err := net.Dial("tcp", dstAddr)

    if err == nil {
        startDetour(id, src, dst, bandwidth, bufferSize)
    } else {
        src.Close()
        log.Println(err.Error())
    }
}

func startDetour(id int, src net.Conn, dst net.Conn, bandwidth int64, bufferSize uint64) {
    log.Println("Opening route", id, ":", src.RemoteAddr().String(), "to", dst.RemoteAddr().String())

    var wg sync.WaitGroup
    wg.Add(1)
    go reroute(&wg, src, dst, bandwidth, bufferSize)
    reroute(nil, dst, src, bandwidth, bufferSize)
    wg.Wait()

    log.Println("Closing route", id, ":", src.RemoteAddr().String(), "to", dst.RemoteAddr().String())
}

func reroute(wg *sync.WaitGroup, src net.Conn, dst net.Conn, bandwidth int64, bufferSize uint64) {
    tb := tokenbucket.New(uint64(bandwidth), 10 * uint64(bandwidth))
    buf := make([]byte, bufferSize)
    defer src.Close()
    defer dst.Close()

    for {
        bytes := tb.Remove(bufferSize)
        if bytes < bufferSize {
            tb.Return(bytes)
            time.Sleep(1 * time.Millisecond)
            continue
        }

        size, err := src.Read(buf)
        if err == nil {
            dst.Write(buf[0:size])
            if size < int(bufferSize) {
                tb.Return(bufferSize - uint64(size))
            }
        } else {
            break
        }
    }

    if wg != nil {
        wg.Done()
    }
}
<commit_msg>fixed HTTP/S routes<commit_after>package main

import (
    "bufio"
    "bytes"
    "encoding/json"
    "flag"
    "fmt"
    "io/ioutil"
    "log"
    "net"
    "net/http"
    "net/url"
    "os"
    "os/signal"
    "strings"
    "sync"
    "syscall"
    "time"
    "tokenbucket"
)

const _VERSION string = "0.2.0"

type Route struct {
    Bandwidth int64 `json:"bandwidth"`    // Bits per second
    Buffersize uint64 `json:"buffersize"` // Bytes
    Inspect bool `json:"inspect"`         // True = proxy, false = reverse proxy
    Src string `json:"src"`
    Dst []string `json:"dst"`
}

type Itinerary struct {
    Map map[string]Route
}

func sigHandler(ch *chan os.Signal) {
    sig := <-*ch
    fmt.Println("Captured sig", sig)
    os.Exit(3)
}

func main() {
    sigs := make(chan os.Signal, 1)
    signal.Notify(sigs,
                  syscall.SIGHUP,
                  syscall.SIGINT,
                  syscall.SIGQUIT,
                  syscall.SIGABRT,
                  syscall.SIGKILL,
                  syscall.SIGSEGV,
                  syscall.SIGTERM)

    log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)

    itineraryFile := flag.String("itinerary", "itinerary.json", "file containing source and destination routes")
    flag.Usage = func() {
        fmt.Fprintf(os.Stderr, "version %s\n", _VERSION)
        fmt.Fprintln(os.Stderr, "usage:")
        flag.PrintDefaults()
    }
    flag.Parse()

    itinerary := loadItinerary(itineraryFile)

    for _, m := range itinerary.Map {
        go intercept(m)
    }

    sigHandler(&sigs)
}

func loadItinerary(fileName *string) Itinerary {

    file, _ := os.Open(*fileName)
    decoder := json.NewDecoder(file)
    itinerary := Itinerary{}
    err := decoder.Decode(&itinerary)
    if err != nil {
        log.Println(err.Error())
    }

    return itinerary
}

func intercept(route Route) {
    listener, err := net.Listen("tcp", route.Src)
    if err == nil {
        log.Println("Listening on", route.Src)
        i := 0
        count := 0

        for {
            con, err := listener.Accept()
            if err == nil {
                count = count + 1

                if route.Inspect { // Proxy mode
                    go findHttpRoute(count, con, route.Bandwidth / 8, route.Buffersize)
                } else { // Load balancer mode
                    // Round-robin for now
                    go findRoute(count, con, route.Dst[i], route.Bandwidth / 8, route.Buffersize)
                    i = (i + 1) % len(route.Dst)
                }
            } else {
                log.Println(err.Error())
            }
        }
        listener.Close()
    } else {
        log.Println(err.Error())
    }
}

func parseHttpRequestUri(header *http.Request) string {
    var uri string
    if header.Method == "HEAD" || header.Method == "PUT" {
        url, err := url.Parse(header.RequestURI)

        if err == nil {
            if strings.ContainsAny(url.Host, ":") {
                uri = url.Host
            } else {
                switch url.Scheme {
                    case "http":
                        uri = url.Host + ":80"
                    case "https":
                        uri = url.Host + ":443"
                    default:
                        uri = url.Host
                }
            }
        } else {
            log.Println(err.Error())
        }
    } else if header.Method == "CONNECT" {
        uri = header.RequestURI
    }

    return uri
}

func findHttpRoute(id int, src net.Conn, bandwidth int64, bufferSize uint64) {
    buf := make([]byte, 65536)
    size, err := src.Read(buf) // Assume one read will include entire HTTP header

    if err == nil {
        reader := bufio.NewReader(strings.NewReader(string(buf[0:size])))
        header, _ := http.ReadRequest(reader)

        dstAddr := parseHttpRequestUri(header)
        dst, err := net.Dial("tcp", dstAddr)

        if err == nil {
            if header.Method == "CONNECT" {
                body := ""
                rsp := &http.Response {
                    Status:        "200 OK",
                    StatusCode:    200,
                    Proto:         "HTTP/1.1",
                    ProtoMajor:    1,
                    ProtoMinor:    1,
                    Body:          ioutil.NopCloser(bytes.NewBufferString(body)),
                    ContentLength: int64(len(body)),
                    Request:       nil,
                    Header:        make(http.Header, 0),
                }
                rspBuf := bytes.NewBuffer(nil)
                rsp.Write(rspBuf)
                src.Write(rspBuf.Bytes())
            } else {
                dst.Write(buf[0:size])
            }
            startDetour(id, src, dst, bandwidth, bufferSize)
        } else {
            src.Close()
            log.Println(err.Error())
        }
    } else {
        src.Close()
        log.Println(err.Error())
    }
}

func findRoute(id int, src net.Conn, dstAddr string, bandwidth int64, bufferSize uint64) {
    dst, err := net.Dial("tcp", dstAddr)

    if err == nil {
        startDetour(id, src, dst, bandwidth, bufferSize)
    } else {
        src.Close()
        log.Println(err.Error())
    }
}

func startDetour(id int, src net.Conn, dst net.Conn, bandwidth int64, bufferSize uint64) {
    log.Println("Opening route", id, ":", src.RemoteAddr().String(), "to", dst.RemoteAddr().String())

    var wg sync.WaitGroup
    wg.Add(1)
    go reroute(&wg, src, dst, bandwidth, bufferSize)
    reroute(nil, dst, src, bandwidth, bufferSize)
    wg.Wait()

    log.Println("Closing route", id, ":", src.RemoteAddr().String(), "to", dst.RemoteAddr().String())
}

func reroute(wg *sync.WaitGroup, src net.Conn, dst net.Conn, bandwidth int64, bufferSize uint64) {
    tb := tokenbucket.New(uint64(bandwidth), 10 * uint64(bandwidth))
    buf := make([]byte, bufferSize)
    defer src.Close()
    defer dst.Close()

    for {
        bytes := tb.Remove(bufferSize)
        if bytes < bufferSize {
            tb.Return(bytes)
            time.Sleep(1 * time.Millisecond)
            continue
        }

        size, err := src.Read(buf)
        if err == nil {
            dst.Write(buf[0:size])
            if size < int(bufferSize) {
                tb.Return(bufferSize - uint64(size))
            }
        } else {
            break
        }
    }

    if wg != nil {
        wg.Done()
    }
}
<|endoftext|>


<commit_before>package airplay

import "github.com/armon/mdns"

// A Device is an AirPlay Device.
type Device struct {
	Name string
	Addr string
	Port int
}

// Devices returns all AirPlay devices in LAN.
func Devices() []Device {
	devices := []Device{}
	entriesCh := make(chan *mdns.ServiceEntry, 4)
	defer close(entriesCh)

	go func() {
		for entry := range entriesCh {
			devices = append(
				devices,
				Device{
					Name: entry.Name,
					Addr: entry.Addr.String(),
					Port: entry.Port,
				},
			)
		}
	}()

	mdns.Lookup("_airplay._tcp", entriesCh)

	return devices
}
<commit_msg>Support IPv6 address AirServer returns.<commit_after>package airplay

import "github.com/armon/mdns"

// A Device is an AirPlay Device.
type Device struct {
	Name string
	Addr string
	Port int
}

// Devices returns all AirPlay devices in LAN.
func Devices() []Device {
	devices := []Device{}
	entriesCh := make(chan *mdns.ServiceEntry, 4)
	defer close(entriesCh)

	go func() {
		for entry := range entriesCh {
			ip := entry.Addr
			var addr string

			if ip.To16() != nil {
				addr = "[" + ip.String() + "]"
			} else {
				addr = ip.String()
			}

			devices = append(
				devices,
				Device{
					Name: entry.Name,
					Addr: addr,
					Port: entry.Port,
				},
			)
		}
	}()

	mdns.Lookup("_airplay._tcp", entriesCh)

	return devices
}
<|endoftext|>


<commit_before>package handlers

import (
	"bytes"
	"encoding/json"
	"io"
	"net/http"
	"strings"

	"github.com/Sirupsen/logrus"
	ctxu "github.com/docker/distribution/context"
	"github.com/gorilla/mux"
	"golang.org/x/net/context"

	"github.com/docker/notary/server/errors"
	"github.com/docker/notary/server/snapshot"
	"github.com/docker/notary/server/storage"
	"github.com/docker/notary/server/timestamp"
	"github.com/docker/notary/tuf/data"
	"github.com/docker/notary/tuf/signed"
	"github.com/docker/notary/tuf/validation"
	"github.com/docker/notary/utils"
)

// MainHandler is the default handler for the server
func MainHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
	// For now it only supports `GET`
	if r.Method != "GET" {
		return errors.ErrGenericNotFound.WithDetail(nil)
	}

	if _, err := w.Write([]byte("{}")); err != nil {
		return errors.ErrUnknown.WithDetail(err)
	}
	return nil
}

// AtomicUpdateHandler will accept multiple TUF files and ensure that the storage
// backend is atomically updated with all the new records.
func AtomicUpdateHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
	defer r.Body.Close()
	vars := mux.Vars(r)
	return atomicUpdateHandler(ctx, w, r, vars)
}

func atomicUpdateHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	gun := vars["imageName"]
	s := ctx.Value("metaStore")
	logger := ctxu.GetLoggerWithField(ctx, gun, "gun")
	store, ok := s.(storage.MetaStore)
	if !ok {
		logger.Error("500 POST unable to retrieve storage")
		return errors.ErrNoStorage.WithDetail(nil)
	}
	cryptoServiceVal := ctx.Value("cryptoService")
	cryptoService, ok := cryptoServiceVal.(signed.CryptoService)
	if !ok {
		logger.Error("500 POST unable to retrieve signing service")
		return errors.ErrNoCryptoService.WithDetail(nil)
	}

	reader, err := r.MultipartReader()
	if err != nil {
		logger.Info("400 POST unable to parse TUF data")
		return errors.ErrMalformedUpload.WithDetail(nil)
	}
	var updates []storage.MetaUpdate
	for {
		part, err := reader.NextPart()
		if err == io.EOF {
			break
		}
		role := strings.TrimSuffix(part.FileName(), ".json")
		if role == "" {
			logger.Info("400 POST empty role")
			return errors.ErrNoFilename.WithDetail(nil)
		} else if !data.ValidRole(role) {
			logger.Infof("400 POST invalid role: %s", role)
			return errors.ErrInvalidRole.WithDetail(role)
		}
		meta := &data.SignedMeta{}
		var input []byte
		inBuf := bytes.NewBuffer(input)
		dec := json.NewDecoder(io.TeeReader(part, inBuf))
		err = dec.Decode(meta)
		if err != nil {
			logger.Info("400 POST malformed update JSON")
			return errors.ErrMalformedJSON.WithDetail(nil)
		}
		version := meta.Signed.Version
		updates = append(updates, storage.MetaUpdate{
			Role:    role,
			Version: version,
			Data:    inBuf.Bytes(),
		})
	}
	updates, err = validateUpdate(cryptoService, gun, updates, store)
	if err != nil {
		serializable, serializableError := validation.NewSerializableError(err)
		if serializableError != nil {
			logger.Info("400 POST error validating update")
			return errors.ErrInvalidUpdate.WithDetail(nil)
		}
		return errors.ErrInvalidUpdate.WithDetail(serializable)
	}
	err = store.UpdateMany(gun, updates)
	if err != nil {
		// If we have an old version error, surface to user with error code
		if _, ok := err.(storage.ErrOldVersion); ok {
			logger.Info("400 POST old version error")
			return errors.ErrOldVersion.WithDetail(err)
		}
		// More generic storage update error, possibly due to attempted rollback
		logger.Errorf("500 POST error applying update request: %v", err)
		return errors.ErrUpdating.WithDetail(nil)
	}
	return nil
}

// GetHandler returns the json for a specified role and GUN.
func GetHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
	defer r.Body.Close()
	vars := mux.Vars(r)
	return getHandler(ctx, w, r, vars)
}

func getHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	gun := vars["imageName"]
	checksum := vars["checksum"]
	tufRole := vars["tufRole"]
	s := ctx.Value("metaStore")

	logger := ctxu.GetLoggerWithField(ctx, gun, "gun")

	store, ok := s.(storage.MetaStore)
	if !ok {
		logger.Error("500 GET: no storage exists")
		return errors.ErrNoStorage.WithDetail(nil)
	}

	lastModified, output, err := getRole(ctx, store, gun, tufRole, checksum)
	if err != nil {
		logger.Infof("404 GET %s role", tufRole)
		return err
	}
	if lastModified != nil {
		// This shouldn't always be true, but in case it is nil, and the last modified headers
		// are not set, the cache control handler should set the last modified date to the beginning
		// of time.
		utils.SetLastModifiedHeader(w.Header(), *lastModified)
	} else {
		logrus.Warnf("Got bytes out for %s's %s (checksum: %s), but missing lastModified date",
			gun, tufRole, checksum)
	}

	w.Write(output)
	return nil
}

// DeleteHandler deletes all data for a GUN. A 200 responses indicates success.
func DeleteHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
	s := ctx.Value("metaStore")
	store, ok := s.(storage.MetaStore)
	if !ok {
		logrus.Error("500 DELETE repository: no storage exists")
		return errors.ErrNoStorage.WithDetail(nil)
	}
	vars := mux.Vars(r)
	gun := vars["imageName"]
	logger := ctxu.GetLoggerWithField(ctx, gun, "gun")
	err := store.Delete(gun)
	if err != nil {
		logger.Error("500 DELETE repository")
		return errors.ErrUnknown.WithDetail(err)
	}
	return nil
}

// GetKeyHandler returns a public key for the specified role, creating a new key-pair
// it if it doesn't yet exist
func GetKeyHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
	defer r.Body.Close()
	vars := mux.Vars(r)
	return getKeyHandler(ctx, w, r, vars)
}

func getKeyHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	gun, ok := vars["imageName"]
	if !ok || gun == "" {
		logrus.Info("400 GET no gun in request")
		return errors.ErrUnknown.WithDetail("no gun")
	}

	logger := ctxu.GetLoggerWithField(ctx, gun, "gun")

	role, ok := vars["tufRole"]
	if !ok || role == "" {
		logger.Info("400 GET no role in request")
		return errors.ErrUnknown.WithDetail("no role")
	}

	s := ctx.Value("metaStore")
	store, ok := s.(storage.MetaStore)
	if !ok || store == nil {
		logger.Error("500 GET storage not configured")
		return errors.ErrNoStorage.WithDetail(nil)
	}
	c := ctx.Value("cryptoService")
	crypto, ok := c.(signed.CryptoService)
	if !ok || crypto == nil {
		logger.Error("500 GET crypto service not configured")
		return errors.ErrNoCryptoService.WithDetail(nil)
	}
	algo := ctx.Value("keyAlgorithm")
	keyAlgo, ok := algo.(string)
	if !ok || keyAlgo == "" {
		logger.Error("500 GET key algorithm not configured")
		return errors.ErrNoKeyAlgorithm.WithDetail(nil)
	}
	keyAlgorithm := keyAlgo

	var (
		key data.PublicKey
		err error
	)
	switch role {
	case data.CanonicalTimestampRole:
		key, err = timestamp.GetOrCreateTimestampKey(gun, store, crypto, keyAlgorithm)
	case data.CanonicalSnapshotRole:
		key, err = snapshot.GetOrCreateSnapshotKey(gun, store, crypto, keyAlgorithm)
	default:
		logger.Infof("400 GET %s key: %v", role, err)
		return errors.ErrInvalidRole.WithDetail(role)
	}
	if err != nil {
		logger.Errorf("500 GET %s key: %v", role, err)
		return errors.ErrUnknown.WithDetail(err)
	}

	out, err := json.Marshal(key)
	if err != nil {
		logger.Errorf("500 GET %s key", role)
		return errors.ErrUnknown.WithDetail(err)
	}
	logger.Debugf("200 GET %s key", role)
	w.Write(out)
	return nil
}

// NotFoundHandler is used as a generic catch all handler to return the ErrMetadataNotFound
// 404 response
func NotFoundHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
	return errors.ErrMetadataNotFound.WithDetail(nil)
}
<commit_msg>Change logrus to logger where possible<commit_after>package handlers

import (
	"bytes"
	"encoding/json"
	"io"
	"net/http"
	"strings"

	ctxu "github.com/docker/distribution/context"
	"github.com/gorilla/mux"
	"golang.org/x/net/context"

	"github.com/docker/notary/server/errors"
	"github.com/docker/notary/server/snapshot"
	"github.com/docker/notary/server/storage"
	"github.com/docker/notary/server/timestamp"
	"github.com/docker/notary/tuf/data"
	"github.com/docker/notary/tuf/signed"
	"github.com/docker/notary/tuf/validation"
	"github.com/docker/notary/utils"
)

// MainHandler is the default handler for the server
func MainHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
	// For now it only supports `GET`
	if r.Method != "GET" {
		return errors.ErrGenericNotFound.WithDetail(nil)
	}

	if _, err := w.Write([]byte("{}")); err != nil {
		return errors.ErrUnknown.WithDetail(err)
	}
	return nil
}

// AtomicUpdateHandler will accept multiple TUF files and ensure that the storage
// backend is atomically updated with all the new records.
func AtomicUpdateHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
	defer r.Body.Close()
	vars := mux.Vars(r)
	return atomicUpdateHandler(ctx, w, r, vars)
}

func atomicUpdateHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	gun := vars["imageName"]
	s := ctx.Value("metaStore")
	logger := ctxu.GetLoggerWithField(ctx, gun, "gun")
	store, ok := s.(storage.MetaStore)
	if !ok {
		logger.Error("500 POST unable to retrieve storage")
		return errors.ErrNoStorage.WithDetail(nil)
	}
	cryptoServiceVal := ctx.Value("cryptoService")
	cryptoService, ok := cryptoServiceVal.(signed.CryptoService)
	if !ok {
		logger.Error("500 POST unable to retrieve signing service")
		return errors.ErrNoCryptoService.WithDetail(nil)
	}

	reader, err := r.MultipartReader()
	if err != nil {
		logger.Info("400 POST unable to parse TUF data")
		return errors.ErrMalformedUpload.WithDetail(nil)
	}
	var updates []storage.MetaUpdate
	for {
		part, err := reader.NextPart()
		if err == io.EOF {
			break
		}
		role := strings.TrimSuffix(part.FileName(), ".json")
		if role == "" {
			logger.Info("400 POST empty role")
			return errors.ErrNoFilename.WithDetail(nil)
		} else if !data.ValidRole(role) {
			logger.Infof("400 POST invalid role: %s", role)
			return errors.ErrInvalidRole.WithDetail(role)
		}
		meta := &data.SignedMeta{}
		var input []byte
		inBuf := bytes.NewBuffer(input)
		dec := json.NewDecoder(io.TeeReader(part, inBuf))
		err = dec.Decode(meta)
		if err != nil {
			logger.Info("400 POST malformed update JSON")
			return errors.ErrMalformedJSON.WithDetail(nil)
		}
		version := meta.Signed.Version
		updates = append(updates, storage.MetaUpdate{
			Role:    role,
			Version: version,
			Data:    inBuf.Bytes(),
		})
	}
	updates, err = validateUpdate(cryptoService, gun, updates, store)
	if err != nil {
		serializable, serializableError := validation.NewSerializableError(err)
		if serializableError != nil {
			logger.Info("400 POST error validating update")
			return errors.ErrInvalidUpdate.WithDetail(nil)
		}
		return errors.ErrInvalidUpdate.WithDetail(serializable)
	}
	err = store.UpdateMany(gun, updates)
	if err != nil {
		// If we have an old version error, surface to user with error code
		if _, ok := err.(storage.ErrOldVersion); ok {
			logger.Info("400 POST old version error")
			return errors.ErrOldVersion.WithDetail(err)
		}
		// More generic storage update error, possibly due to attempted rollback
		logger.Errorf("500 POST error applying update request: %v", err)
		return errors.ErrUpdating.WithDetail(nil)
	}
	return nil
}

// GetHandler returns the json for a specified role and GUN.
func GetHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
	defer r.Body.Close()
	vars := mux.Vars(r)
	return getHandler(ctx, w, r, vars)
}

func getHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	gun := vars["imageName"]
	checksum := vars["checksum"]
	tufRole := vars["tufRole"]
	s := ctx.Value("metaStore")

	logger := ctxu.GetLoggerWithField(ctx, gun, "gun")

	store, ok := s.(storage.MetaStore)
	if !ok {
		logger.Error("500 GET: no storage exists")
		return errors.ErrNoStorage.WithDetail(nil)
	}

	lastModified, output, err := getRole(ctx, store, gun, tufRole, checksum)
	if err != nil {
		logger.Infof("404 GET %s role", tufRole)
		return err
	}
	if lastModified != nil {
		// This shouldn't always be true, but in case it is nil, and the last modified headers
		// are not set, the cache control handler should set the last modified date to the beginning
		// of time.
		utils.SetLastModifiedHeader(w.Header(), *lastModified)
	} else {
		logger.Warnf("Got bytes out for %s's %s (checksum: %s), but missing lastModified date",
			gun, tufRole, checksum)
	}

	w.Write(output)
	return nil
}

// DeleteHandler deletes all data for a GUN. A 200 responses indicates success.
func DeleteHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
	vars := mux.Vars(r)
	gun := vars["imageName"]
	logger := ctxu.GetLoggerWithField(ctx, gun, "gun")
	s := ctx.Value("metaStore")
	store, ok := s.(storage.MetaStore)
	if !ok {
		logger.Error("500 DELETE repository: no storage exists")
		return errors.ErrNoStorage.WithDetail(nil)
	}
	err := store.Delete(gun)
	if err != nil {
		logger.Error("500 DELETE repository")
		return errors.ErrUnknown.WithDetail(err)
	}
	return nil
}

// GetKeyHandler returns a public key for the specified role, creating a new key-pair
// it if it doesn't yet exist
func GetKeyHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
	defer r.Body.Close()
	vars := mux.Vars(r)
	return getKeyHandler(ctx, w, r, vars)
}

func getKeyHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	gun, ok := vars["imageName"]
	logger := ctxu.GetLoggerWithField(ctx, gun, "gun")
	if !ok || gun == "" {
		logger.Info("400 GET no gun in request")
		return errors.ErrUnknown.WithDetail("no gun")
	}

	role, ok := vars["tufRole"]
	if !ok || role == "" {
		logger.Info("400 GET no role in request")
		return errors.ErrUnknown.WithDetail("no role")
	}

	s := ctx.Value("metaStore")
	store, ok := s.(storage.MetaStore)
	if !ok || store == nil {
		logger.Error("500 GET storage not configured")
		return errors.ErrNoStorage.WithDetail(nil)
	}
	c := ctx.Value("cryptoService")
	crypto, ok := c.(signed.CryptoService)
	if !ok || crypto == nil {
		logger.Error("500 GET crypto service not configured")
		return errors.ErrNoCryptoService.WithDetail(nil)
	}
	algo := ctx.Value("keyAlgorithm")
	keyAlgo, ok := algo.(string)
	if !ok || keyAlgo == "" {
		logger.Error("500 GET key algorithm not configured")
		return errors.ErrNoKeyAlgorithm.WithDetail(nil)
	}
	keyAlgorithm := keyAlgo

	var (
		key data.PublicKey
		err error
	)
	switch role {
	case data.CanonicalTimestampRole:
		key, err = timestamp.GetOrCreateTimestampKey(gun, store, crypto, keyAlgorithm)
	case data.CanonicalSnapshotRole:
		key, err = snapshot.GetOrCreateSnapshotKey(gun, store, crypto, keyAlgorithm)
	default:
		logger.Infof("400 GET %s key: %v", role, err)
		return errors.ErrInvalidRole.WithDetail(role)
	}
	if err != nil {
		logger.Errorf("500 GET %s key: %v", role, err)
		return errors.ErrUnknown.WithDetail(err)
	}

	out, err := json.Marshal(key)
	if err != nil {
		logger.Errorf("500 GET %s key", role)
		return errors.ErrUnknown.WithDetail(err)
	}
	logger.Debugf("200 GET %s key", role)
	w.Write(out)
	return nil
}

// NotFoundHandler is used as a generic catch all handler to return the ErrMetadataNotFound
// 404 response
func NotFoundHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
	return errors.ErrMetadataNotFound.WithDetail(nil)
}
<|endoftext|>


<commit_before>package heroku

import (
	"fmt"
	"net/http"
	"time"

	"github.com/remind101/empire"
	"github.com/remind101/empire/pkg/heroku"
	"github.com/remind101/empire/pkg/hijack"
	"github.com/remind101/empire/pkg/stdcopy"
	streamhttp "github.com/remind101/empire/pkg/stream/http"
	"github.com/remind101/empire/pkg/timex"
	"github.com/remind101/empire/server/auth"
)

type Dyno heroku.Dyno

func newDyno(task *empire.Task) *Dyno {
	return &Dyno{
		Command:   task.Command.String(),
		Type:      task.Type,
		Name:      task.Name,
		Host:      heroku.Host{Id: task.Host.ID},
		State:     task.State,
		Size:      task.Constraints.String(),
		UpdatedAt: task.UpdatedAt,
	}
}

func newDynos(tasks []*empire.Task) []*Dyno {
	dynos := make([]*Dyno, len(tasks))

	for i := 0; i < len(tasks); i++ {
		dynos[i] = newDyno(tasks[i])
	}

	return dynos
}

func (h *Server) GetProcesses(w http.ResponseWriter, r *http.Request) error {
	ctx := r.Context()

	a, err := h.findApp(r)
	if err != nil {
		return err
	}

	// Retrieve tasks
	js, err := h.Tasks(ctx, a)
	if err != nil {
		return err
	}

	w.WriteHeader(200)
	return Encode(w, newDynos(js))
}

type PostProcessForm struct {
	Command string              `json:"command"`
	Attach  bool                `json:"attach"`
	Env     map[string]string   `json:"env"`
	Size    *empire.Constraints `json:"size"`
}

func (h *Server) PostProcess(w http.ResponseWriter, r *http.Request) error {
	ctx := r.Context()

	var form PostProcessForm

	a, err := h.findApp(r)
	if err != nil {
		return err
	}

	m, err := findMessage(r)
	if err != nil {
		return err
	}

	if err := Decode(r, &form); err != nil {
		return err
	}

	command, err := empire.ParseCommand(form.Command)
	if err != nil {
		return err
	}

	opts := empire.RunOpts{
		User:        auth.UserFromContext(ctx),
		App:         a,
		Command:     command,
		Env:         form.Env,
		Constraints: form.Size,
		Message:     m,
	}

	if form.Attach {
		multiplex := r.Header.Get("X-Multiplex") != ""

		header := http.Header{}
		if multiplex {
			header.Set("Content-Type", "application/vnd.empire.stdcopy-stream")
		} else {
			header.Set("Content-Type", "application/vnd.empire.raw-stream")
		}

		stream := &hijack.HijackReadWriter{
			Response: w,
			Header:   header,
		}
		defer stream.Close()
		// Prevent the ELB idle connection timeout to close the connection.
		defer close(streamhttp.Heartbeat(stream, 10*time.Second))

		opts.Stdin = stream

		if multiplex {
			opts.Stdout = stdcopy.NewStdWriter(stream, stdcopy.Stdout)
			opts.Stderr = stdcopy.NewStdWriter(stream, stdcopy.Stderr)
		} else {
			// Backwards compatibility for older clients that don't
			// know how to de-multiplex a stdcopy stream. For these
			// clients, stdout/stderr are merged together.
			opts.Stdout = stream
			opts.Stderr = stream
		}

		if err := h.Run(ctx, opts); err != nil {
			if stream.Hijacked {
				fmt.Fprintf(stream, "%v\r", err)
				return nil
			}
			return err
		}
	} else {
		if err := h.Run(ctx, opts); err != nil {
			return err
		}

		dyno := &heroku.Dyno{
			Name:      "run",
			Command:   form.Command,
			CreatedAt: timex.Now(),
		}

		w.WriteHeader(201)
		return Encode(w, dyno)
	}

	return nil
}

func (h *Server) DeleteProcesses(w http.ResponseWriter, r *http.Request) error {
	ctx := r.Context()

	vars := Vars(r)
	pid := vars["pid"]

	if vars["ptype"] != "" {
		return errNotImplemented("Restarting a process type is currently not implemented.")
	}

	a, err := h.findApp(r)
	if err != nil {
		return err
	}

	m, err := findMessage(r)
	if err != nil {
		return err
	}

	if err := h.Restart(ctx, empire.RestartOpts{
		User:    auth.UserFromContext(ctx),
		App:     a,
		PID:     pid,
		Message: m,
	}); err != nil {
		return err
	}

	return NoContent(w)
}
<commit_msg>Fix emp run lockup in 0.13 (#1125)<commit_after>package heroku

import (
	"fmt"
	"net/http"

	"github.com/remind101/empire"
	"github.com/remind101/empire/pkg/heroku"
	"github.com/remind101/empire/pkg/hijack"
	"github.com/remind101/empire/pkg/stdcopy"
	"github.com/remind101/empire/pkg/timex"
	"github.com/remind101/empire/server/auth"
)

type Dyno heroku.Dyno

func newDyno(task *empire.Task) *Dyno {
	return &Dyno{
		Command:   task.Command.String(),
		Type:      task.Type,
		Name:      task.Name,
		Host:      heroku.Host{Id: task.Host.ID},
		State:     task.State,
		Size:      task.Constraints.String(),
		UpdatedAt: task.UpdatedAt,
	}
}

func newDynos(tasks []*empire.Task) []*Dyno {
	dynos := make([]*Dyno, len(tasks))

	for i := 0; i < len(tasks); i++ {
		dynos[i] = newDyno(tasks[i])
	}

	return dynos
}

func (h *Server) GetProcesses(w http.ResponseWriter, r *http.Request) error {
	ctx := r.Context()

	a, err := h.findApp(r)
	if err != nil {
		return err
	}

	// Retrieve tasks
	js, err := h.Tasks(ctx, a)
	if err != nil {
		return err
	}

	w.WriteHeader(200)
	return Encode(w, newDynos(js))
}

type PostProcessForm struct {
	Command string              `json:"command"`
	Attach  bool                `json:"attach"`
	Env     map[string]string   `json:"env"`
	Size    *empire.Constraints `json:"size"`
}

func (h *Server) PostProcess(w http.ResponseWriter, r *http.Request) error {
	ctx := r.Context()

	var form PostProcessForm

	a, err := h.findApp(r)
	if err != nil {
		return err
	}

	m, err := findMessage(r)
	if err != nil {
		return err
	}

	if err := Decode(r, &form); err != nil {
		return err
	}

	command, err := empire.ParseCommand(form.Command)
	if err != nil {
		return err
	}

	opts := empire.RunOpts{
		User:        auth.UserFromContext(ctx),
		App:         a,
		Command:     command,
		Env:         form.Env,
		Constraints: form.Size,
		Message:     m,
	}

	if form.Attach {
		multiplex := r.Header.Get("X-Multiplex") != ""

		header := http.Header{}
		if multiplex {
			header.Set("Content-Type", "application/vnd.empire.stdcopy-stream")
		} else {
			header.Set("Content-Type", "application/vnd.empire.raw-stream")
		}

		stream := &hijack.HijackReadWriter{
			Response: w,
			Header:   header,
		}
		defer stream.Close()

		opts.Stdin = stream

		if multiplex {
			opts.Stdout = stdcopy.NewStdWriter(stream, stdcopy.Stdout)
			opts.Stderr = stdcopy.NewStdWriter(stream, stdcopy.Stderr)
		} else {
			// Backwards compatibility for older clients that don't
			// know how to de-multiplex a stdcopy stream. For these
			// clients, stdout/stderr are merged together.
			opts.Stdout = stream
			opts.Stderr = stream
		}

		if err := h.Run(ctx, opts); err != nil {
			if stream.Hijacked {
				fmt.Fprintf(stream, "%v\r", err)
				return nil
			}
			return err
		}
	} else {
		if err := h.Run(ctx, opts); err != nil {
			return err
		}

		dyno := &heroku.Dyno{
			Name:      "run",
			Command:   form.Command,
			CreatedAt: timex.Now(),
		}

		w.WriteHeader(201)
		return Encode(w, dyno)
	}

	return nil
}

func (h *Server) DeleteProcesses(w http.ResponseWriter, r *http.Request) error {
	ctx := r.Context()

	vars := Vars(r)
	pid := vars["pid"]

	if vars["ptype"] != "" {
		return errNotImplemented("Restarting a process type is currently not implemented.")
	}

	a, err := h.findApp(r)
	if err != nil {
		return err
	}

	m, err := findMessage(r)
	if err != nil {
		return err
	}

	if err := h.Restart(ctx, empire.RestartOpts{
		User:    auth.UserFromContext(ctx),
		App:     a,
		PID:     pid,
		Message: m,
	}); err != nil {
		return err
	}

	return NoContent(w)
}
<|endoftext|>