<commit_before>/* https://leetcode.com/problems/minimum-size-subarray-sum/description/
Given an array of n positive integers and a positive integer s, find the minimal length of a contiguous subarray of which the sum ≥ s. If there isn't one, return 0 instead.

For example, given the array [2,3,1,2,4,3] and s = 7,
the subarray [4,3] has the minimal length under the problem constraint.

More practice:
If you have figured out the O(n) solution, try coding another solution of which the time complexity is O(n log n).
*/

package leetcode

// Two Pointers
func minSubArrayLen(s int, nums []int) int {
	sums := make([]int, len(nums)+1)

	l, r, minLen := 0, 0, 0
	for r < len(sums) {
		if sums[r]-sums[l] >= s {
			if tmp := r - l; tmp < minLen || minLen == 0 {
				minLen = tmp
			}
			l++
		} else if r++; r < len(sums) {
			sums[r] = sums[r-1] + nums[r-1]
		}
	}
	return minLen
}
<commit_msg>another thinking<commit_after>/* https://leetcode.com/problems/minimum-size-subarray-sum/description/
Given an array of n positive integers and a positive integer s, find the minimal length of a contiguous subarray of which the sum ≥ s. If there isn't one, return 0 instead.

For example, given the array [2,3,1,2,4,3] and s = 7,
the subarray [4,3] has the minimal length under the problem constraint.

More practice:
If you have figured out the O(n) solution, try coding another solution of which the time complexity is O(n log n).
*/

package leetcode

// Two Pointers
func minSubArrayLen(s int, nums []int) int {
	if len(nums) == 0 {
		return 0
	}

	minLen, sum := 0, nums[0]
	for l, r := 0, 0; r < len(nums); {
		if sum >= s {
			if tmp := r - l + 1; tmp < minLen || minLen == 0 {
				minLen = tmp
			}
			sum -= nums[l]
			l++
		} else if r++; r < len(nums) {
			sum += nums[r]
		}
	}
	return minLen
}
<|endoftext|>


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

package core

import (
	"os"
	"os/signal"
)

// EventLoopWithInterrupt starts an instance of the agent created with NewAgent().
// Agent is stopped when <closeChan> is closed or a user interrupt (SIGINT)
// is received.
func EventLoopWithInterrupt(agent *Agent, closeChan chan struct{}) error {
	err := agent.Start()
	if err != nil {
		agent.Error("Error loading core: ", err)
		return err
	}

	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, os.Interrupt)
	select {
	case <-sigChan:
		agent.Println("Interrupt received, returning.")
	case <-closeChan:
	}

	err = agent.Stop()
	if err != nil {
		agent.Errorf("Agent stop error '%+v'", err)
	}
	return err
}
<commit_msg>Handle SIGTERM in EventLoopWithInterrupt<commit_after>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package core

import (
	"os"
	"os/signal"
	"syscall"
)

// EventLoopWithInterrupt starts an instance of the agent created with NewAgent().
// Agent is stopped when <closeChan> is closed, a user interrupt (SIGINT), or a
// terminate signal (SIGTERM) is received.
func EventLoopWithInterrupt(agent *Agent, closeChan chan struct{}) error {
	err := agent.Start()
	if err != nil {
		agent.Error("Error loading core: ", err)
		return err
	}

	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, os.Interrupt)
	signal.Notify(sigChan, syscall.SIGTERM)
	select {
	case <-sigChan:
		agent.Println("Interrupt received, returning.")
	case <-closeChan:
	}

	err = agent.Stop()
	if err != nil {
		agent.Errorf("Agent stop error '%+v'", err)
	}
	return err
}
<|endoftext|>


<commit_before>package setup

import (
	"fmt"
	"net"
	"os"

	"github.com/miekg/coredns/middleware"
	"github.com/miekg/coredns/middleware/file"
)

// File sets up the file middleware.
func File(c *Controller) (middleware.Middleware, error) {
	zones, err := fileParse(c)
	if err != nil {
		return nil, err
	}

	// Add startup functions to notify the master.
	for _, n := range zones.Names {
		if len(zones.Z[n].TransferTo) > 0 {
			c.Startup = append(c.Startup, func() error {
				zones.Z[n].StartupOnce.Do(func() {
					if len(zones.Z[n].TransferTo) > 0 {
						zones.Z[n].Notify()
					}
				})
			})
		}
	}

	return func(next middleware.Handler) middleware.Handler {
		return file.File{Next: next, Zones: zones}
	}, nil

}

func fileParse(c *Controller) (file.Zones, error) {
	z := make(map[string]*file.Zone)
	names := []string{}
	for c.Next() {
		if c.Val() == "file" {
			// file db.file [origin]
			if !c.NextArg() {
				return file.Zones{}, c.ArgErr()
			}
			fileName := c.Val()

			origins := []string{c.ServerBlockHosts[c.ServerBlockHostIndex]}
			args := c.RemainingArgs()
			if len(args) > 0 {
				origins = args
			}

			reader, err := os.Open(fileName)
			if err != nil {
				return file.Zones{}, err
			}

			for i, _ := range origins {
				origins[i] = middleware.Host(origins[i]).Normalize()
				zone, err := file.Parse(reader, origins[i], fileName)
				if err == nil {
					z[origins[i]] = zone
				}
				names = append(names, origins[i])
			}

			for c.NextBlock() {
				t, _, e := parseTransfer(c)
				if e != nil {
					return file.Zones{}, e
				}
				// discard from, here, maybe check and show log when we do?
				for _, origin := range origins {
					if t != nil {
						z[origin].TransferTo = append(z[origin].TransferTo, t...)
					}
				}
			}
		}
	}
	return file.Zones{Z: z, Names: names}, nil
}

// transfer to [address...]
func parseTransfer(c *Controller) (tos, froms []string, err error) {
	what := c.Val()
	if !c.NextArg() {
		return nil, nil, c.ArgErr()
	}
	value := c.Val()
	switch what {
	case "transfer":
		if !c.NextArg() {
			return nil, nil, c.ArgErr()
		}
		if value == "to" {
			tos := c.RemainingArgs()
			for i, _ := range tos {
				if x := net.ParseIP(tos[i]); x == nil {
					return nil, nil, fmt.Errorf("must specify an IP addres: `%s'", tos[i])
				}
				if tos[i] != "*" {
					tos[i] = middleware.Addr(tos[i]).Normalize()
				}
			}
		}
		if value == "from" {
			froms := c.RemainingArgs()
			for i, _ := range froms {
				if x := net.ParseIP(froms[i]); x == nil {
					return nil, nil, fmt.Errorf("must specify an IP addres: `%s'", froms[i])
				}
				if froms[i] != "*" {
					froms[i] = middleware.Addr(froms[i]).Normalize()
				} else {
					return nil, nil, fmt.Errorf("can't use '*' in transfer from")
				}
			}
		}
	}
	return
}
<commit_msg>Fix build<commit_after>package setup

import (
	"fmt"
	"net"
	"os"

	"github.com/miekg/coredns/middleware"
	"github.com/miekg/coredns/middleware/file"
)

// File sets up the file middleware.
func File(c *Controller) (middleware.Middleware, error) {
	zones, err := fileParse(c)
	if err != nil {
		return nil, err
	}

	// Add startup functions to notify the master.
	for _, n := range zones.Names {
		if len(zones.Z[n].TransferTo) > 0 {
			c.Startup = append(c.Startup, func() error {
				zones.Z[n].StartupOnce.Do(func() {
					if len(zones.Z[n].TransferTo) > 0 {
						zones.Z[n].Notify()
					}
				})
				return nil
			})
		}
	}

	return func(next middleware.Handler) middleware.Handler {
		return file.File{Next: next, Zones: zones}
	}, nil

}

func fileParse(c *Controller) (file.Zones, error) {
	z := make(map[string]*file.Zone)
	names := []string{}
	for c.Next() {
		if c.Val() == "file" {
			// file db.file [origin]
			if !c.NextArg() {
				return file.Zones{}, c.ArgErr()
			}
			fileName := c.Val()

			origins := []string{c.ServerBlockHosts[c.ServerBlockHostIndex]}
			args := c.RemainingArgs()
			if len(args) > 0 {
				origins = args
			}

			reader, err := os.Open(fileName)
			if err != nil {
				return file.Zones{}, err
			}

			for i, _ := range origins {
				origins[i] = middleware.Host(origins[i]).Normalize()
				zone, err := file.Parse(reader, origins[i], fileName)
				if err == nil {
					z[origins[i]] = zone
				}
				names = append(names, origins[i])
			}

			for c.NextBlock() {
				t, _, e := parseTransfer(c)
				if e != nil {
					return file.Zones{}, e
				}
				// discard from, here, maybe check and show log when we do?
				for _, origin := range origins {
					if t != nil {
						z[origin].TransferTo = append(z[origin].TransferTo, t...)
					}
				}
			}
		}
	}
	return file.Zones{Z: z, Names: names}, nil
}

// transfer to [address...]
func parseTransfer(c *Controller) (tos, froms []string, err error) {
	what := c.Val()
	if !c.NextArg() {
		return nil, nil, c.ArgErr()
	}
	value := c.Val()
	switch what {
	case "transfer":
		if !c.NextArg() {
			return nil, nil, c.ArgErr()
		}
		if value == "to" {
			tos := c.RemainingArgs()
			for i, _ := range tos {
				if x := net.ParseIP(tos[i]); x == nil {
					return nil, nil, fmt.Errorf("must specify an IP addres: `%s'", tos[i])
				}
				if tos[i] != "*" {
					tos[i] = middleware.Addr(tos[i]).Normalize()
				}
			}
		}
		if value == "from" {
			froms := c.RemainingArgs()
			for i, _ := range froms {
				if x := net.ParseIP(froms[i]); x == nil {
					return nil, nil, fmt.Errorf("must specify an IP addres: `%s'", froms[i])
				}
				if froms[i] != "*" {
					froms[i] = middleware.Addr(froms[i]).Normalize()
				} else {
					return nil, nil, fmt.Errorf("can't use '*' in transfer from")
				}
			}
		}
	}
	return
}
<|endoftext|>


<commit_before>/*
Copyright 2014 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 errors

import (
	"fmt"
	"net/http"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors"
)

// StatusError is an error intended for consumption by a REST API server; it can also be
// reconstructed by clients from a REST response. Public to allow easy type switches.
type StatusError struct {
	ErrStatus api.Status
}

var _ error = &StatusError{}

// Error implements the Error interface.
func (e *StatusError) Error() string {
	return e.ErrStatus.Message
}

// Status allows access to e's status without having to know the detailed workings
// of StatusError. Used by pkg/apiserver.
func (e *StatusError) Status() api.Status {
	return e.ErrStatus
}

// UnexpectedObjectError can be returned by FromObject if it's passed a non-status object.
type UnexpectedObjectError struct {
	Object runtime.Object
}

// Error returns an error message describing 'u'.
func (u *UnexpectedObjectError) Error() string {
	return fmt.Sprintf("unexpected object: %v", u.Object)
}

// FromObject generates an StatusError from an api.Status, if that is the type of obj; otherwise,
// returns an UnexpecteObjectError.
func FromObject(obj runtime.Object) error {
	switch t := obj.(type) {
	case *api.Status:
		return &StatusError{*t}
	}
	return &UnexpectedObjectError{obj}
}

// NewNotFound returns a new error which indicates that the resource of the kind and the name was not found.
func NewNotFound(kind, name string) error {
	return &StatusError{api.Status{
		Status: api.StatusFailure,
		Code:   http.StatusNotFound,
		Reason: api.StatusReasonNotFound,
		Details: &api.StatusDetails{
			Kind: kind,
			ID:   name,
		},
		Message: fmt.Sprintf("%s %q not found", kind, name),
	}}
}

// NewAlreadyExists returns an error indicating the item requested exists by that identifier.
func NewAlreadyExists(kind, name string) error {
	return &StatusError{api.Status{
		Status: api.StatusFailure,
		Code:   http.StatusConflict,
		Reason: api.StatusReasonAlreadyExists,
		Details: &api.StatusDetails{
			Kind: kind,
			ID:   name,
		},
		Message: fmt.Sprintf("%s %q already exists", kind, name),
	}}
}

// NewForbidden returns an error indicating the requested action was forbidden
func NewForbidden(kind, name string, err error) error {
	return &StatusError{api.Status{
		Status: api.StatusFailure,
		Code:   http.StatusForbidden,
		Reason: api.StatusReasonForbidden,
		Details: &api.StatusDetails{
			Kind: kind,
			ID:   name,
		},
		Message: fmt.Sprintf("%s %q is forbidden", kind, name),
	}}
}

// NewConflict returns an error indicating the item can't be updated as provided.
func NewConflict(kind, name string, err error) error {
	return &StatusError{api.Status{
		Status: api.StatusFailure,
		Code:   http.StatusConflict,
		Reason: api.StatusReasonConflict,
		Details: &api.StatusDetails{
			Kind: kind,
			ID:   name,
		},
		Message: fmt.Sprintf("%s %q cannot be updated: %v", kind, name, err),
	}}
}

// NewInvalid returns an error indicating the item is invalid and cannot be processed.
func NewInvalid(kind, name string, errs ValidationErrorList) error {
	causes := make([]api.StatusCause, 0, len(errs))
	for i := range errs {
		if err, ok := errs[i].(*ValidationError); ok {
			causes = append(causes, api.StatusCause{
				Type:    api.CauseType(err.Type),
				Message: err.Error(),
				Field:   err.Field,
			})
		}
	}
	return &StatusError{api.Status{
		Status: api.StatusFailure,
		Code:   422, // RFC 4918: StatusUnprocessableEntity
		Reason: api.StatusReasonInvalid,
		Details: &api.StatusDetails{
			Kind:   kind,
			ID:     name,
			Causes: causes,
		},
		Message: fmt.Sprintf("%s %q is invalid: %v", kind, name, errors.NewAggregate(errs)),
	}}
}

// NewBadRequest creates an error that indicates that the request is invalid and can not be processed.
func NewBadRequest(reason string) error {
	return &StatusError{api.Status{
		Status: api.StatusFailure,
		Code:   http.StatusBadRequest,
		Reason: api.StatusReasonBadRequest,
		Details: &api.StatusDetails{
			Causes: []api.StatusCause{
				{Message: reason},
			},
		},
	}}
}

// NewMethodNotSupported returns an error indicating the requested action is not supported on this kind.
func NewMethodNotSupported(kind, action string) error {
	return &StatusError{api.Status{
		Status: api.StatusFailure,
		Code:   http.StatusMethodNotAllowed,
		Reason: api.StatusReasonMethodNotAllowed,
		Details: &api.StatusDetails{
			Kind: kind,
		},
		Message: fmt.Sprintf("%s is not supported on resources of kind %q", action, kind),
	}}
}

// NewInternalError returns an error indicating the item is invalid and cannot be processed.
func NewInternalError(err error) error {
	return &StatusError{api.Status{
		Status: api.StatusFailure,
		Code:   http.StatusInternalServerError,
		Reason: api.StatusReasonInternalError,
		Details: &api.StatusDetails{
			Causes: []api.StatusCause{{Message: err.Error()}},
		},
		Message: fmt.Sprintf("Internal error occurred: %v", err),
	}}
}

// IsNotFound returns true if the specified error was created by NewNotFoundErr.
func IsNotFound(err error) bool {
	return reasonForError(err) == api.StatusReasonNotFound
}

// IsAlreadyExists determines if the err is an error which indicates that a specified resource already exists.
func IsAlreadyExists(err error) bool {
	return reasonForError(err) == api.StatusReasonAlreadyExists
}

// IsConflict determines if the err is an error which indicates the provided update conflicts.
func IsConflict(err error) bool {
	return reasonForError(err) == api.StatusReasonConflict
}

// IsInvalid determines if the err is an error which indicates the provided resource is not valid.
func IsInvalid(err error) bool {
	return reasonForError(err) == api.StatusReasonInvalid
}

// IsMethodNotSupported determines if the err is an error which indicates the provided action could not
// be performed because it is not supported by the server.
func IsMethodNotSupported(err error) bool {
	return reasonForError(err) == api.StatusReasonMethodNotAllowed
}

// IsBadRequest determines if err is an error which indicates that the request is invalid.
func IsBadRequest(err error) bool {
	return reasonForError(err) == api.StatusReasonBadRequest
}

func reasonForError(err error) api.StatusReason {
	switch t := err.(type) {
	case *StatusError:
		return t.ErrStatus.Reason
	}
	return api.StatusReasonUnknown
}
<commit_msg>Forbidden should not swallow the underlying error<commit_after>/*
Copyright 2014 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 errors

import (
	"fmt"
	"net/http"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors"
)

// StatusError is an error intended for consumption by a REST API server; it can also be
// reconstructed by clients from a REST response. Public to allow easy type switches.
type StatusError struct {
	ErrStatus api.Status
}

var _ error = &StatusError{}

// Error implements the Error interface.
func (e *StatusError) Error() string {
	return e.ErrStatus.Message
}

// Status allows access to e's status without having to know the detailed workings
// of StatusError. Used by pkg/apiserver.
func (e *StatusError) Status() api.Status {
	return e.ErrStatus
}

// UnexpectedObjectError can be returned by FromObject if it's passed a non-status object.
type UnexpectedObjectError struct {
	Object runtime.Object
}

// Error returns an error message describing 'u'.
func (u *UnexpectedObjectError) Error() string {
	return fmt.Sprintf("unexpected object: %v", u.Object)
}

// FromObject generates an StatusError from an api.Status, if that is the type of obj; otherwise,
// returns an UnexpecteObjectError.
func FromObject(obj runtime.Object) error {
	switch t := obj.(type) {
	case *api.Status:
		return &StatusError{*t}
	}
	return &UnexpectedObjectError{obj}
}

// NewNotFound returns a new error which indicates that the resource of the kind and the name was not found.
func NewNotFound(kind, name string) error {
	return &StatusError{api.Status{
		Status: api.StatusFailure,
		Code:   http.StatusNotFound,
		Reason: api.StatusReasonNotFound,
		Details: &api.StatusDetails{
			Kind: kind,
			ID:   name,
		},
		Message: fmt.Sprintf("%s %q not found", kind, name),
	}}
}

// NewAlreadyExists returns an error indicating the item requested exists by that identifier.
func NewAlreadyExists(kind, name string) error {
	return &StatusError{api.Status{
		Status: api.StatusFailure,
		Code:   http.StatusConflict,
		Reason: api.StatusReasonAlreadyExists,
		Details: &api.StatusDetails{
			Kind: kind,
			ID:   name,
		},
		Message: fmt.Sprintf("%s %q already exists", kind, name),
	}}
}

// NewForbidden returns an error indicating the requested action was forbidden
func NewForbidden(kind, name string, err error) error {
	return &StatusError{api.Status{
		Status: api.StatusFailure,
		Code:   http.StatusForbidden,
		Reason: api.StatusReasonForbidden,
		Details: &api.StatusDetails{
			Kind: kind,
			ID:   name,
		},
		Message: fmt.Sprintf("%s %q is forbidden: %v", kind, name, err),
	}}
}

// NewConflict returns an error indicating the item can't be updated as provided.
func NewConflict(kind, name string, err error) error {
	return &StatusError{api.Status{
		Status: api.StatusFailure,
		Code:   http.StatusConflict,
		Reason: api.StatusReasonConflict,
		Details: &api.StatusDetails{
			Kind: kind,
			ID:   name,
		},
		Message: fmt.Sprintf("%s %q cannot be updated: %v", kind, name, err),
	}}
}

// NewInvalid returns an error indicating the item is invalid and cannot be processed.
func NewInvalid(kind, name string, errs ValidationErrorList) error {
	causes := make([]api.StatusCause, 0, len(errs))
	for i := range errs {
		if err, ok := errs[i].(*ValidationError); ok {
			causes = append(causes, api.StatusCause{
				Type:    api.CauseType(err.Type),
				Message: err.Error(),
				Field:   err.Field,
			})
		}
	}
	return &StatusError{api.Status{
		Status: api.StatusFailure,
		Code:   422, // RFC 4918: StatusUnprocessableEntity
		Reason: api.StatusReasonInvalid,
		Details: &api.StatusDetails{
			Kind:   kind,
			ID:     name,
			Causes: causes,
		},
		Message: fmt.Sprintf("%s %q is invalid: %v", kind, name, errors.NewAggregate(errs)),
	}}
}

// NewBadRequest creates an error that indicates that the request is invalid and can not be processed.
func NewBadRequest(reason string) error {
	return &StatusError{api.Status{
		Status: api.StatusFailure,
		Code:   http.StatusBadRequest,
		Reason: api.StatusReasonBadRequest,
		Details: &api.StatusDetails{
			Causes: []api.StatusCause{
				{Message: reason},
			},
		},
	}}
}

// NewMethodNotSupported returns an error indicating the requested action is not supported on this kind.
func NewMethodNotSupported(kind, action string) error {
	return &StatusError{api.Status{
		Status: api.StatusFailure,
		Code:   http.StatusMethodNotAllowed,
		Reason: api.StatusReasonMethodNotAllowed,
		Details: &api.StatusDetails{
			Kind: kind,
		},
		Message: fmt.Sprintf("%s is not supported on resources of kind %q", action, kind),
	}}
}

// NewInternalError returns an error indicating the item is invalid and cannot be processed.
func NewInternalError(err error) error {
	return &StatusError{api.Status{
		Status: api.StatusFailure,
		Code:   http.StatusInternalServerError,
		Reason: api.StatusReasonInternalError,
		Details: &api.StatusDetails{
			Causes: []api.StatusCause{{Message: err.Error()}},
		},
		Message: fmt.Sprintf("Internal error occurred: %v", err),
	}}
}

// IsNotFound returns true if the specified error was created by NewNotFoundErr.
func IsNotFound(err error) bool {
	return reasonForError(err) == api.StatusReasonNotFound
}

// IsAlreadyExists determines if the err is an error which indicates that a specified resource already exists.
func IsAlreadyExists(err error) bool {
	return reasonForError(err) == api.StatusReasonAlreadyExists
}

// IsConflict determines if the err is an error which indicates the provided update conflicts.
func IsConflict(err error) bool {
	return reasonForError(err) == api.StatusReasonConflict
}

// IsInvalid determines if the err is an error which indicates the provided resource is not valid.
func IsInvalid(err error) bool {
	return reasonForError(err) == api.StatusReasonInvalid
}

// IsMethodNotSupported determines if the err is an error which indicates the provided action could not
// be performed because it is not supported by the server.
func IsMethodNotSupported(err error) bool {
	return reasonForError(err) == api.StatusReasonMethodNotAllowed
}

// IsBadRequest determines if err is an error which indicates that the request is invalid.
func IsBadRequest(err error) bool {
	return reasonForError(err) == api.StatusReasonBadRequest
}

func reasonForError(err error) api.StatusReason {
	switch t := err.(type) {
	case *StatusError:
		return t.ErrStatus.Reason
	}
	return api.StatusReasonUnknown
}
<|endoftext|>


<commit_before>package clusteryaml

import (
	"io/ioutil"

	"context"

	"github.com/rancher/rancher/pkg/settings"
	"github.com/rancher/rke/cluster"
	"github.com/rancher/rke/services"
	"github.com/rancher/types/apis/management.cattle.io/v3"
	"gopkg.in/yaml.v2"
)

const (
	localCluster = "/etc/rancher/cluster.yml"
)

func LocalConfig() (*v3.RancherKubernetesEngineConfig, error) {
	rkeConfig := &v3.RancherKubernetesEngineConfig{}

	content, err := ioutil.ReadFile(localCluster)
	if err == nil {
		if err := yaml.Unmarshal(content, rkeConfig); err != nil {
			return nil, err
		}
	}

	roles := []string{
		services.ControlRole,
	}

	if len(rkeConfig.Services.Etcd.ExternalURLs) == 0 {
		roles = append(roles, services.ETCDRole)
	}

	if rkeConfig.Services.KubeAPI.ExtraArgs == nil {
		rkeConfig.Services.KubeAPI.ExtraArgs = map[string]string{}
	}

	rkeConfig.Services.KubeAPI.ExtraArgs["advertise-address"] = "10.233.0.1"
	rkeConfig.Services.KubeAPI.ExtraArgs["bind-address"] = "127.0.0.1"
	rkeConfig.SystemImages = v3.K8sVersionToRKESystemImages[v3.K8sV1_8]
	rkeConfig.SystemImages.Kubernetes = settings.AgentImage.Get()
	rkeConfig.IgnoreDockerVersion = true
	rkeConfig.Nodes = []v3.RKEConfigNode{
		{
			HostnameOverride: "master",
			Address:          "127.0.0.1",
			User:             "root",
			Role:             roles,
			DockerSocket:     "/var/run/docker.sock",
		},
	}

	c, err := cluster.ParseCluster(context.Background(),
		rkeConfig,
		"",
		"",
		nil,
		nil,
		nil)
	if err != nil {
		return nil, err
	}

	return &c.RancherKubernetesEngineConfig, nil
}
<commit_msg>Update const name<commit_after>package clusteryaml

import (
	"io/ioutil"

	"context"

	"github.com/rancher/rancher/pkg/settings"
	"github.com/rancher/rke/cluster"
	"github.com/rancher/rke/services"
	"github.com/rancher/types/apis/management.cattle.io/v3"
	"gopkg.in/yaml.v2"
)

const (
	localCluster = "/etc/rancher/cluster.yml"
)

func LocalConfig() (*v3.RancherKubernetesEngineConfig, error) {
	rkeConfig := &v3.RancherKubernetesEngineConfig{}

	content, err := ioutil.ReadFile(localCluster)
	if err == nil {
		if err := yaml.Unmarshal(content, rkeConfig); err != nil {
			return nil, err
		}
	}

	roles := []string{
		services.ControlRole,
	}

	if len(rkeConfig.Services.Etcd.ExternalURLs) == 0 {
		roles = append(roles, services.ETCDRole)
	}

	if rkeConfig.Services.KubeAPI.ExtraArgs == nil {
		rkeConfig.Services.KubeAPI.ExtraArgs = map[string]string{}
	}

	rkeConfig.Services.KubeAPI.ExtraArgs["advertise-address"] = "10.233.0.1"
	rkeConfig.Services.KubeAPI.ExtraArgs["bind-address"] = "127.0.0.1"
	rkeConfig.SystemImages = v3.K8sVersionToRKESystemImages[v3.K8sV18]
	rkeConfig.SystemImages.Kubernetes = settings.AgentImage.Get()
	rkeConfig.IgnoreDockerVersion = true
	rkeConfig.Nodes = []v3.RKEConfigNode{
		{
			HostnameOverride: "master",
			Address:          "127.0.0.1",
			User:             "root",
			Role:             roles,
			DockerSocket:     "/var/run/docker.sock",
		},
	}

	c, err := cluster.ParseCluster(context.Background(),
		rkeConfig,
		"",
		"",
		nil,
		nil,
		nil)
	if err != nil {
		return nil, err
	}

	return &c.RancherKubernetesEngineConfig, nil
}
<|endoftext|>


<commit_before>// Copyright 2017-2019 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package dhclient provides a unified interface for interfacing with both
// DHCPv4 and DHCPv6 clients.
package dhclient

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/url"
	"os"
	"sync"
	"time"

	"github.com/insomniacslk/dhcp/dhcpv4"
	"github.com/insomniacslk/dhcp/dhcpv4/nclient4"
	"github.com/insomniacslk/dhcp/dhcpv6"
	"github.com/insomniacslk/dhcp/dhcpv6/nclient6"
	"github.com/vishvananda/netlink"
	"golang.org/x/sys/unix"
)

const linkUpAttempt = 30 * time.Second

// isIpv6LinkReady returns true iff the interface has a link-local address
// which is not tentative.
func isIpv6LinkReady(l netlink.Link) (bool, error) {
	addrs, err := netlink.AddrList(l, netlink.FAMILY_V6)
	if err != nil {
		return false, err
	}
	for _, addr := range addrs {
		if addr.IP.IsLinkLocalUnicast() && (addr.Flags&unix.IFA_F_TENTATIVE == 0) {
			if addr.Flags&unix.IFA_F_DADFAILED != 0 {
				log.Printf("DADFAILED for %v, continuing anyhow", addr.IP)
			}
			return true, nil
		}
	}
	return false, nil
}

// IfUp ensures the given network interface is up and returns the link object.
func IfUp(ifname string) (netlink.Link, error) {
	start := time.Now()
	for time.Since(start) < linkUpAttempt {
		// Note that it may seem odd to keep trying the LinkByName
		// operation, but consider that a hotplug device such as USB
		// ethernet can just vanish.
		iface, err := netlink.LinkByName(ifname)
		if err != nil {
			return nil, fmt.Errorf("cannot get interface %q by name: %v", ifname, err)
		}

		if iface.Attrs().Flags&net.FlagUp == net.FlagUp {
			return iface, nil
		}

		if err := netlink.LinkSetUp(iface); err != nil {
			return nil, fmt.Errorf("interface %q: %v can't make it up: %v", ifname, iface, err)
		}
		time.Sleep(100 * time.Millisecond)
	}

	return nil, fmt.Errorf("link %q still down after %d seconds", ifname, linkUpAttempt)
}

// Configure4 adds IP addresses, routes, and DNS servers to the system.
func Configure4(iface netlink.Link, packet *dhcpv4.DHCPv4) error {
	p := NewPacket4(iface, packet)
	return p.Configure()
}

// Configure6 adds IPv6 addresses, routes, and DNS servers to the system.
func Configure6(iface netlink.Link, packet *dhcpv6.Message) error {
	p := NewPacket6(iface, packet)

	l := p.Lease()
	if l == nil {
		return fmt.Errorf("no lease returned")
	}

	// Add the address to the iface.
	dst := &netlink.Addr{
		IPNet: &net.IPNet{
			IP:   l.IPv6Addr,
			Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::")),
		},
		PreferedLft: int(l.PreferredLifetime),
		ValidLft:    int(l.ValidLifetime),
		// Optimistic DAD (Duplicate Address Detection) means we can
		// use the address before DAD is complete. The DHCP server's
		// job was to give us a unique IP so there is little risk of a
		// collision.
		Flags: unix.IFA_F_OPTIMISTIC,
	}
	if err := netlink.AddrReplace(iface, dst); err != nil {
		if os.IsExist(err) {
			return fmt.Errorf("add/replace %s to %v: %v", dst, iface, err)
		}
	}

	if ips := p.DNS(); ips != nil {
		if err := WriteDNSSettings(ips); err != nil {
			return err
		}
	}
	return nil
}

// WriteDNSSettings writes the given IPs as nameservers to resolv.conf.
func WriteDNSSettings(ips []net.IP) error {
	rc := &bytes.Buffer{}
	for _, ip := range ips {
		rc.WriteString(fmt.Sprintf("nameserver %s\n", ip))
	}
	return ioutil.WriteFile("/etc/resolv.conf", rc.Bytes(), 0644)
}

type Lease interface {
	fmt.Stringer
	Configure() error
	Boot() (*url.URL, error)
	Link() netlink.Link
}

func lease4(ctx context.Context, iface netlink.Link, timeout time.Duration, retries int) (Lease, error) {
	client, err := nclient4.New(iface.Attrs().Name,
		nclient4.WithTimeout(timeout),
		nclient4.WithRetry(retries))
	if err != nil {
		return nil, err
	}

	log.Printf("Attempting to get DHCPv4 lease on %s", iface.Attrs().Name)
	_, p, err := client.Request(ctx, dhcpv4.WithNetboot)
	if err != nil {
		return nil, err
	}

	packet := NewPacket4(iface, p)
	log.Printf("Got DHCPv4 lease on %s: %v", iface.Attrs().Name, p.Summary())
	return packet, nil
}

func lease6(ctx context.Context, iface netlink.Link, timeout time.Duration, retries int) (Lease, error) {
	// For ipv6, we cannot bind to the port until Duplicate Address
	// Detection (DAD) is complete which is indicated by the link being no
	// longer marked as "tentative". This usually takes about a second.
	for {
		if ready, err := isIpv6LinkReady(iface); err != nil {
			return nil, err
		} else if ready {
			break
		}
		select {
		case <-time.After(100 * time.Millisecond):
			continue
		case <-ctx.Done():
			return nil, errors.New("timeout after waiting for a non-tentative IPv6 address")
		}
	}

	client, err := nclient6.New(iface.Attrs().Name,
		nclient6.WithTimeout(timeout),
		nclient6.WithRetry(retries))
	if err != nil {
		return nil, err
	}

	log.Printf("Attempting to get DHCPv6 lease on %s", iface.Attrs().Name)
	p, err := client.RapidSolicit(ctx, dhcpv6.WithNetboot)
	if err != nil {
		return nil, err
	}

	packet := NewPacket6(iface, p)
	log.Printf("Got DHCPv6 lease on %s: %v", iface.Attrs().Name, p.Summary())
	return packet, nil
}

type Result struct {
	Interface netlink.Link
	Lease     Lease
	Err       error
}

func SendRequests(ctx context.Context, ifs []netlink.Link, timeout time.Duration, retries int, ipv4, ipv6 bool) chan *Result {
	// Yeah, this is a hack, until we can cancel all leases in progress.
	r := make(chan *Result, 3*len(ifs))

	var wg sync.WaitGroup
	for _, iface := range ifs {
		wg.Add(1)
		go func(iface netlink.Link) {
			defer wg.Done()

			log.Printf("Bringing up interface %s...", iface.Attrs().Name)
			if _, err := IfUp(iface.Attrs().Name); err != nil {
				log.Printf("Could not bring up interface %s: %v", iface.Attrs().Name, err)
				return
			}

			if ipv4 {
				wg.Add(1)
				go func(iface netlink.Link) {
					defer wg.Done()
					lease, err := lease4(ctx, iface, timeout, retries)
					r <- &Result{iface, lease, err}
				}(iface)
			}

			if ipv6 {
				wg.Add(1)
				go func(iface netlink.Link) {
					defer wg.Done()
					lease, err := lease6(ctx, iface, timeout, retries)
					r <- &Result{iface, lease, err}
				}(iface)
			}
		}(iface)
	}

	go func() {
		wg.Wait()
		close(r)
	}()
	return r
}
<commit_msg>Add Vendor Class Identifier for dhclient.<commit_after>// Copyright 2017-2019 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package dhclient provides a unified interface for interfacing with both
// DHCPv4 and DHCPv6 clients.
package dhclient

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/url"
	"os"
	"sync"
	"time"

	"github.com/insomniacslk/dhcp/dhcpv4"
	"github.com/insomniacslk/dhcp/dhcpv4/nclient4"
	"github.com/insomniacslk/dhcp/dhcpv6"
	"github.com/insomniacslk/dhcp/dhcpv6/nclient6"
	"github.com/vishvananda/netlink"
	"golang.org/x/sys/unix"
)

const linkUpAttempt = 30 * time.Second

// isIpv6LinkReady returns true iff the interface has a link-local address
// which is not tentative.
func isIpv6LinkReady(l netlink.Link) (bool, error) {
	addrs, err := netlink.AddrList(l, netlink.FAMILY_V6)
	if err != nil {
		return false, err
	}
	for _, addr := range addrs {
		if addr.IP.IsLinkLocalUnicast() && (addr.Flags&unix.IFA_F_TENTATIVE == 0) {
			if addr.Flags&unix.IFA_F_DADFAILED != 0 {
				log.Printf("DADFAILED for %v, continuing anyhow", addr.IP)
			}
			return true, nil
		}
	}
	return false, nil
}

// IfUp ensures the given network interface is up and returns the link object.
func IfUp(ifname string) (netlink.Link, error) {
	start := time.Now()
	for time.Since(start) < linkUpAttempt {
		// Note that it may seem odd to keep trying the LinkByName
		// operation, but consider that a hotplug device such as USB
		// ethernet can just vanish.
		iface, err := netlink.LinkByName(ifname)
		if err != nil {
			return nil, fmt.Errorf("cannot get interface %q by name: %v", ifname, err)
		}

		if iface.Attrs().Flags&net.FlagUp == net.FlagUp {
			return iface, nil
		}

		if err := netlink.LinkSetUp(iface); err != nil {
			return nil, fmt.Errorf("interface %q: %v can't make it up: %v", ifname, iface, err)
		}
		time.Sleep(100 * time.Millisecond)
	}

	return nil, fmt.Errorf("link %q still down after %d seconds", ifname, linkUpAttempt)
}

// Configure4 adds IP addresses, routes, and DNS servers to the system.
func Configure4(iface netlink.Link, packet *dhcpv4.DHCPv4) error {
	p := NewPacket4(iface, packet)
	return p.Configure()
}

// Configure6 adds IPv6 addresses, routes, and DNS servers to the system.
func Configure6(iface netlink.Link, packet *dhcpv6.Message) error {
	p := NewPacket6(iface, packet)

	l := p.Lease()
	if l == nil {
		return fmt.Errorf("no lease returned")
	}

	// Add the address to the iface.
	dst := &netlink.Addr{
		IPNet: &net.IPNet{
			IP:   l.IPv6Addr,
			Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::")),
		},
		PreferedLft: int(l.PreferredLifetime),
		ValidLft:    int(l.ValidLifetime),
		// Optimistic DAD (Duplicate Address Detection) means we can
		// use the address before DAD is complete. The DHCP server's
		// job was to give us a unique IP so there is little risk of a
		// collision.
		Flags: unix.IFA_F_OPTIMISTIC,
	}
	if err := netlink.AddrReplace(iface, dst); err != nil {
		if os.IsExist(err) {
			return fmt.Errorf("add/replace %s to %v: %v", dst, iface, err)
		}
	}

	if ips := p.DNS(); ips != nil {
		if err := WriteDNSSettings(ips); err != nil {
			return err
		}
	}
	return nil
}

// WriteDNSSettings writes the given IPs as nameservers to resolv.conf.
func WriteDNSSettings(ips []net.IP) error {
	rc := &bytes.Buffer{}
	for _, ip := range ips {
		rc.WriteString(fmt.Sprintf("nameserver %s\n", ip))
	}
	return ioutil.WriteFile("/etc/resolv.conf", rc.Bytes(), 0644)
}

type Lease interface {
	fmt.Stringer
	Configure() error
	Boot() (*url.URL, error)
	Link() netlink.Link
}

func lease4(ctx context.Context, iface netlink.Link, timeout time.Duration, retries int) (Lease, error) {
	client, err := nclient4.New(iface.Attrs().Name,
		nclient4.WithTimeout(timeout),
		nclient4.WithRetry(retries))
	if err != nil {
		return nil, err
	}

	log.Printf("Attempting to get DHCPv4 lease on %s", iface.Attrs().Name)
	_, p, err := client.Request(ctx, dhcpv4.WithNetboot,
		dhcpv4.WithOption(dhcpv4.OptClassIdentifier("PXE UROOT")),
		dhcpv4.WithRequestedOptions(dhcpv4.OptionSubnetMask))
	if err != nil {
		return nil, err
	}

	packet := NewPacket4(iface, p)
	log.Printf("Got DHCPv4 lease on %s: %v", iface.Attrs().Name, p.Summary())
	return packet, nil
}

func lease6(ctx context.Context, iface netlink.Link, timeout time.Duration, retries int) (Lease, error) {
	// For ipv6, we cannot bind to the port until Duplicate Address
	// Detection (DAD) is complete which is indicated by the link being no
	// longer marked as "tentative". This usually takes about a second.
	for {
		if ready, err := isIpv6LinkReady(iface); err != nil {
			return nil, err
		} else if ready {
			break
		}
		select {
		case <-time.After(100 * time.Millisecond):
			continue
		case <-ctx.Done():
			return nil, errors.New("timeout after waiting for a non-tentative IPv6 address")
		}
	}

	client, err := nclient6.New(iface.Attrs().Name,
		nclient6.WithTimeout(timeout),
		nclient6.WithRetry(retries))
	if err != nil {
		return nil, err
	}

	log.Printf("Attempting to get DHCPv6 lease on %s", iface.Attrs().Name)
	p, err := client.RapidSolicit(ctx, dhcpv6.WithNetboot)
	if err != nil {
		return nil, err
	}

	packet := NewPacket6(iface, p)
	log.Printf("Got DHCPv6 lease on %s: %v", iface.Attrs().Name, p.Summary())
	return packet, nil
}

type Result struct {
	Interface netlink.Link
	Lease     Lease
	Err       error
}

func SendRequests(ctx context.Context, ifs []netlink.Link, timeout time.Duration, retries int, ipv4, ipv6 bool) chan *Result {
	// Yeah, this is a hack, until we can cancel all leases in progress.
	r := make(chan *Result, 3*len(ifs))

	var wg sync.WaitGroup
	for _, iface := range ifs {
		wg.Add(1)
		go func(iface netlink.Link) {
			defer wg.Done()

			log.Printf("Bringing up interface %s...", iface.Attrs().Name)
			if _, err := IfUp(iface.Attrs().Name); err != nil {
				log.Printf("Could not bring up interface %s: %v", iface.Attrs().Name, err)
				return
			}

			if ipv4 {
				wg.Add(1)
				go func(iface netlink.Link) {
					defer wg.Done()
					lease, err := lease4(ctx, iface, timeout, retries)
					r <- &Result{iface, lease, err}
				}(iface)
			}

			if ipv6 {
				wg.Add(1)
				go func(iface netlink.Link) {
					defer wg.Done()
					lease, err := lease6(ctx, iface, timeout, retries)
					r <- &Result{iface, lease, err}
				}(iface)
			}
		}(iface)
	}

	go func() {
		wg.Wait()
		close(r)
	}()
	return r
}
<|endoftext|>


<commit_before>/*
	Copyright 2019 Brian Bauer

   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 filepath

import (
	"fmt"
	"net/http"
	"os"
	"os/user"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

/*
FilePath turns a single date path into the paths of all
	relevant files for that date
*/
type FilePath struct {
	FilePath chan string
	basePath string
	wg       sync.WaitGroup
}

/*
ChannelListener should be run in a goroutine and will receive paths on the FilePath
	channel.  It will retrieve the data for that path and save it to a file in the
	location specified.  Once the channel is closed, the goroutine exits
*/
func (fP *FilePath) ChannelListener(client *http.Client) {
	for inputPath := range fP.FilePath {
		fmt.Printf("\tRequesting %s\n", inputPath)
		fP.wg.Add(1)
		time.Sleep(2 * time.Second)
		resp, err := client.Get(inputPath)
		if err != nil {
			fmt.Println(err.Error())
		} else if i := strings.Index(inputPath, "gid_"); i >= 0 {
			outputPath := filepath.Join(fP.basePath, strings.Replace(inputPath[i:], "/", "_", -1))
			fP.writeFile(outputPath, resp)
		}
		fP.wg.Done()
	}
}

func (fP *FilePath) writeFile(filePath string, resp *http.Response) {
	f, err := os.OpenFile(filePath, os.O_CREATE|os.O_APPEND|os.O_RDWR, os.ModePerm)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer f.Close()
	resp.Write(f)
}

/*
Init will create all channels
*/
func (fP *FilePath) Init(output string) {
	fP.FilePath = make(chan string)
	usr, err := user.Current()
	if err != nil {
		fmt.Println("Unable to determine user storage location")
	}
	if len(output) == 0 {
		output = filepath.Join(usr.HomeDir, "baseball")
	}
	fP.basePath = filepath.Join(output, "gameday/raw/")
	err = os.MkdirAll(fP.basePath, 0740)
	if err != nil {
		fmt.Println("Unable to validate storage location")
	}
	fmt.Printf("Storage Location: %s\n", fP.basePath)
}

/*
Done will close the DatePath channel, signalling that we are done
*/
func (fP *FilePath) Done() {
	fP.wg.Wait()
	close(fP.FilePath)
}
<commit_msg>Increase time between requests<commit_after>/*
	Copyright 2019 Brian Bauer

   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 filepath

import (
	"fmt"
	"net/http"
	"os"
	"os/user"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

/*
FilePath turns a single date path into the paths of all
	relevant files for that date
*/
type FilePath struct {
	FilePath chan string
	basePath string
	wg       sync.WaitGroup
}

/*
ChannelListener should be run in a goroutine and will receive paths on the FilePath
	channel.  It will retrieve the data for that path and save it to a file in the
	location specified.  Once the channel is closed, the goroutine exits
*/
func (fP *FilePath) ChannelListener(client *http.Client) {
	for inputPath := range fP.FilePath {
		fmt.Printf("\tRequesting %s\n", inputPath)
		fP.wg.Add(1)
		time.Sleep(4 * time.Second)
		resp, err := client.Get(inputPath)
		if err != nil {
			fmt.Println(err.Error())
		} else if i := strings.Index(inputPath, "gid_"); i >= 0 {
			outputPath := filepath.Join(fP.basePath, strings.Replace(inputPath[i:], "/", "_", -1))
			fP.writeFile(outputPath, resp)
		}
		fP.wg.Done()
	}
}

func (fP *FilePath) writeFile(filePath string, resp *http.Response) {
	f, err := os.OpenFile(filePath, os.O_CREATE|os.O_APPEND|os.O_RDWR, os.ModePerm)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer f.Close()
	resp.Write(f)
}

/*
Init will create all channels
*/
func (fP *FilePath) Init(output string) {
	fP.FilePath = make(chan string)
	usr, err := user.Current()
	if err != nil {
		fmt.Println("Unable to determine user storage location")
	}
	if len(output) == 0 {
		output = filepath.Join(usr.HomeDir, "baseball")
	}
	fP.basePath = filepath.Join(output, "gameday/raw/")
	err = os.MkdirAll(fP.basePath, 0740)
	if err != nil {
		fmt.Println("Unable to validate storage location")
	}
	fmt.Printf("Storage Location: %s\n", fP.basePath)
}

/*
Done will close the DatePath channel, signalling that we are done
*/
func (fP *FilePath) Done() {
	fP.wg.Wait()
	close(fP.FilePath)
}
<|endoftext|>


<commit_before>// Copyright 2019 The GCR Cleaner 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 gcrcleaner

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"time"
)

const (
	contentTypeHeader = "Content-Type"
	contentTypeJSON   = "application/json"
)

// Server is a cleaning server.
type Server struct {
	cleaner *Cleaner
}

// NewServer creates a new server for handler functions.
func NewServer(cleaner *Cleaner) (*Server, error) {
	if cleaner == nil {
		return nil, fmt.Errorf("missing cleaner")
	}

	return &Server{
		cleaner: cleaner,
	}, nil
}

// PubSubHandler is an http handler that invokes the cleaner from a pubsub
// request. Unlike an HTTP request, the pubsub endpoint always returns a success
// unless the pubsub message is malformed.
func (s *Server) PubSubHandler(cache Cache) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var m pubsubMessage
		if err := json.NewDecoder(r.Body).Decode(&m); err != nil {
			err = fmt.Errorf("failed to decode pubsub message: %w", err)
			s.handleError(w, err, 400)
			return
		}

		// PubSub is "at least once" delivery. The cleaner is idempotent, but
		// let's try to prevent unnecessary work by not processing messages we've
		// already received.
		msgID := m.Subscription + "/" + m.Message.ID
		if exists := cache.Insert(msgID); exists {
			log.Printf("already processed message %s", msgID)
			w.WriteHeader(204)
			return
		}

		if len(m.Message.Data) == 0 {
			err := fmt.Errorf("missing data in pubsub payload")
			s.handleError(w, err, 400)
			return
		}

		// Start a goroutine to delete the images
		body := ioutil.NopCloser(bytes.NewReader(m.Message.Data))
		go func() {
			if _, _, err := s.clean(body); err != nil {
				log.Printf("error async: %s", err.Error())
			}
		}()

		w.WriteHeader(204)
	}
}

// HTTPHandler is an http handler that invokes the cleaner with the given
// parameters.
func (s *Server) HTTPHandler() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		deleted, status, err := s.clean(r.Body)
		if err != nil {
			s.handleError(w, err, status)
			return
		}

		b, err := json.Marshal(&cleanResp{
			Count: len(deleted),
			Refs:  deleted,
		})
		if err != nil {
			err = fmt.Errorf("failed to marshal JSON errors: %w", err)
			s.handleError(w, err, 500)
			return
		}

		w.WriteHeader(200)
		w.Header().Set(contentTypeHeader, contentTypeJSON)
		fmt.Fprint(w, string(b))
	}
}

// clean reads the given body as JSON and starts a cleaner instance.
func (s *Server) clean(r io.ReadCloser) ([]string, int, error) {
	var p Payload
	if err := json.NewDecoder(r).Decode(&p); err != nil {
		return nil, 500, fmt.Errorf("failed to decode payload as JSON: %w", err)
	}

	repo := p.Repo

	// Convert duration to a negative value, since we're about to "add" it to the
	// since time.
	sub := time.Duration(p.Grace)
	if p.Grace > 0 {
		sub = sub * -1
	}

	since := time.Now().UTC().Add(sub)
	allowTagged := p.AllowTagged
	keep := p.Keep

	log.Printf("deleting refs for %s since %s\n", repo, since)

	deleted, err := s.cleaner.Clean(repo, since, allowTagged, keep)
	if err != nil {
		return nil, 400, fmt.Errorf("failed to clean: %w", err)
	}

	log.Printf("deleted %d refs for %s", len(deleted), repo)

	return deleted, 200, nil
}

// handleError returns a JSON-formatted error message
func (s *Server) handleError(w http.ResponseWriter, err error, status int) {
	log.Printf("error %d: %s", status, err.Error())

	b, err := json.Marshal(&errorResp{Error: err.Error()})
	if err != nil {
		err = fmt.Errorf("failed to marshal JSON errors: %w", err)
		http.Error(w, err.Error(), 500)
		return
	}

	w.WriteHeader(status)
	w.Header().Set(contentTypeHeader, contentTypeJSON)
	fmt.Fprint(w, string(b))
}

// Payload is the expected incoming payload format.
type Payload struct {
	// Repo is the name of the repo in the format gcr.io/foo/bar
	Repo string `json:"repo"`

	// Grace is a time.Duration value indicating how much grade period should be
	// given to new, untagged layers. The default is no grace.
	Grace duration `json:"grace"`

	// AllowTagged is a Boolean value determine if tagged images are allowed
	// to be deleted.
	AllowTagged bool `json:"allow_tagged"`

	// Keep is the minimum number of images to keep.
	Keep int `json:"keep"`
}

type pubsubMessage struct {
	Message struct {
		Data []byte `json:"data"`
		ID   string `json:"message_id"`
	} `json:"message"`
	Subscription string `json:"subscription"`
}

type cleanResp struct {
	Count int      `json:"count"`
	Refs  []string `json:"refs"`
}

type errorResp struct {
	Error string `json:"error"`
}

type duration time.Duration

func (d duration) MarshalJSON() ([]byte, error) {
	return json.Marshal(time.Duration(d).String())
}

func (d *duration) UnmarshalJSON(b []byte) error {
	var v interface{}
	if err := json.Unmarshal(b, &v); err != nil {
		return err
	}

	switch val := v.(type) {
	case float64:
		*d = duration(time.Duration(val))
		return nil
	case string:
		s, err := time.ParseDuration(val)
		if err != nil {
			return nil
		}
		*d = duration(s)
		return nil
	default:
		return fmt.Errorf("invalid duration type %T", val)
	}
}
<commit_msg>Return error on invalid duration<commit_after>// Copyright 2019 The GCR Cleaner 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 gcrcleaner

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"time"
)

const (
	contentTypeHeader = "Content-Type"
	contentTypeJSON   = "application/json"
)

// Server is a cleaning server.
type Server struct {
	cleaner *Cleaner
}

// NewServer creates a new server for handler functions.
func NewServer(cleaner *Cleaner) (*Server, error) {
	if cleaner == nil {
		return nil, fmt.Errorf("missing cleaner")
	}

	return &Server{
		cleaner: cleaner,
	}, nil
}

// PubSubHandler is an http handler that invokes the cleaner from a pubsub
// request. Unlike an HTTP request, the pubsub endpoint always returns a success
// unless the pubsub message is malformed.
func (s *Server) PubSubHandler(cache Cache) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var m pubsubMessage
		if err := json.NewDecoder(r.Body).Decode(&m); err != nil {
			err = fmt.Errorf("failed to decode pubsub message: %w", err)
			s.handleError(w, err, 400)
			return
		}

		// PubSub is "at least once" delivery. The cleaner is idempotent, but
		// let's try to prevent unnecessary work by not processing messages we've
		// already received.
		msgID := m.Subscription + "/" + m.Message.ID
		if exists := cache.Insert(msgID); exists {
			log.Printf("already processed message %s", msgID)
			w.WriteHeader(204)
			return
		}

		if len(m.Message.Data) == 0 {
			err := fmt.Errorf("missing data in pubsub payload")
			s.handleError(w, err, 400)
			return
		}

		// Start a goroutine to delete the images
		body := ioutil.NopCloser(bytes.NewReader(m.Message.Data))
		go func() {
			if _, _, err := s.clean(body); err != nil {
				log.Printf("error async: %s", err.Error())
			}
		}()

		w.WriteHeader(204)
	}
}

// HTTPHandler is an http handler that invokes the cleaner with the given
// parameters.
func (s *Server) HTTPHandler() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		deleted, status, err := s.clean(r.Body)
		if err != nil {
			s.handleError(w, err, status)
			return
		}

		b, err := json.Marshal(&cleanResp{
			Count: len(deleted),
			Refs:  deleted,
		})
		if err != nil {
			err = fmt.Errorf("failed to marshal JSON errors: %w", err)
			s.handleError(w, err, 500)
			return
		}

		w.WriteHeader(200)
		w.Header().Set(contentTypeHeader, contentTypeJSON)
		fmt.Fprint(w, string(b))
	}
}

// clean reads the given body as JSON and starts a cleaner instance.
func (s *Server) clean(r io.ReadCloser) ([]string, int, error) {
	var p Payload
	if err := json.NewDecoder(r).Decode(&p); err != nil {
		return nil, 500, fmt.Errorf("failed to decode payload as JSON: %w", err)
	}

	repo := p.Repo

	// Convert duration to a negative value, since we're about to "add" it to the
	// since time.
	sub := time.Duration(p.Grace)
	if p.Grace > 0 {
		sub = sub * -1
	}

	since := time.Now().UTC().Add(sub)
	allowTagged := p.AllowTagged
	keep := p.Keep

	log.Printf("deleting refs for %s since %s\n", repo, since)

	deleted, err := s.cleaner.Clean(repo, since, allowTagged, keep)
	if err != nil {
		return nil, 400, fmt.Errorf("failed to clean: %w", err)
	}

	log.Printf("deleted %d refs for %s", len(deleted), repo)

	return deleted, 200, nil
}

// handleError returns a JSON-formatted error message
func (s *Server) handleError(w http.ResponseWriter, err error, status int) {
	log.Printf("error %d: %s", status, err.Error())

	b, err := json.Marshal(&errorResp{Error: err.Error()})
	if err != nil {
		err = fmt.Errorf("failed to marshal JSON errors: %w", err)
		http.Error(w, err.Error(), 500)
		return
	}

	w.WriteHeader(status)
	w.Header().Set(contentTypeHeader, contentTypeJSON)
	fmt.Fprint(w, string(b))
}

// Payload is the expected incoming payload format.
type Payload struct {
	// Repo is the name of the repo in the format gcr.io/foo/bar
	Repo string `json:"repo"`

	// Grace is a time.Duration value indicating how much grade period should be
	// given to new, untagged layers. The default is no grace.
	Grace duration `json:"grace"`

	// AllowTagged is a Boolean value determine if tagged images are allowed
	// to be deleted.
	AllowTagged bool `json:"allow_tagged"`

	// Keep is the minimum number of images to keep.
	Keep int `json:"keep"`
}

type pubsubMessage struct {
	Message struct {
		Data []byte `json:"data"`
		ID   string `json:"message_id"`
	} `json:"message"`
	Subscription string `json:"subscription"`
}

type cleanResp struct {
	Count int      `json:"count"`
	Refs  []string `json:"refs"`
}

type errorResp struct {
	Error string `json:"error"`
}

type duration time.Duration

func (d duration) MarshalJSON() ([]byte, error) {
	return json.Marshal(time.Duration(d).String())
}

func (d *duration) UnmarshalJSON(b []byte) error {
	var v interface{}
	if err := json.Unmarshal(b, &v); err != nil {
		return err
	}

	switch val := v.(type) {
	case float64:
		*d = duration(time.Duration(val))
		return nil
	case string:
		s, err := time.ParseDuration(val)
		if err != nil {
			return err
		}
		*d = duration(s)
		return nil
	default:
		return fmt.Errorf("invalid duration type %T", val)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Jetstack cert-manager contributors.

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 acme

import (
	"bytes"
	"context"
	"crypto"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"fmt"
	"hash/fnv"
	"time"

	"github.com/golang/glog"
	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/selection"
	utilerrors "k8s.io/apimachinery/pkg/util/errors"

	"github.com/jetstack/cert-manager/pkg/acme"
	"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
	"github.com/jetstack/cert-manager/pkg/issuer"
	"github.com/jetstack/cert-manager/pkg/util/errors"
	"github.com/jetstack/cert-manager/pkg/util/kube"
	"github.com/jetstack/cert-manager/pkg/util/pki"
)

const (
	createOrderWaitDuration = time.Minute * 5
)

var (
	certificateGvk = v1alpha1.SchemeGroupVersion.WithKind("Certificate")
)

func (a *Acme) Issue(ctx context.Context, crt *v1alpha1.Certificate) (issuer.IssueResponse, error) {
	key, generated, err := a.getCertificatePrivateKey(crt)
	if err != nil {
		glog.Errorf("Error getting certificate private key: %v", err)
		return issuer.IssueResponse{}, err
	}
	if generated {
		// If we have generated a new private key, we return here to ensure we
		// successfully persist the key before creating any CSRs with it.
		glog.V(4).Infof("Storing new certificate private key for %s/%s", crt.Namespace, crt.Name)

		keyPem, err := pki.EncodePrivateKey(key)
		if err != nil {
			return issuer.IssueResponse{}, err
		}

		return issuer.IssueResponse{
			PrivateKey: keyPem,
		}, nil
	}

	// Initially, we do not set the csr on the order resource we build.
	// This is to save having the overhead of generating a new CSR in the case
	// where the Order resource is up to date already, and also because we have
	// not actually read the existing certificate private key yet to ensure it
	// exists.
	expectedOrder, err := buildOrder(crt, nil)
	if err != nil {
		return issuer.IssueResponse{}, err
	}

	// Cleanup Order resources that are owned by this Certificate but are not
	// up to date (i.e. do not match the requirements on the Certificate).
	// Because the order name returned by buildOrder is a hash of its spec, we
	// can simply delete all order resources that are owned by us that do not
	// have the same name.
	err = a.cleanupOwnedOrders(crt, expectedOrder.Name)
	if err != nil {
		glog.Errorf("Error cleaning up old orders: %v", err)
		return issuer.IssueResponse{}, err
	}

	// Obtain the existing Order for this Certificate from the API server.
	// If it does not exist, we continue on as it will be created from
	// the generated expectedOrder.
	existingOrder, err := a.orderLister.Orders(expectedOrder.Namespace).Get(expectedOrder.Name)
	if err != nil && !apierrors.IsNotFound(err) {
		glog.Errorf("Error getting existing Order resource: %v", err)
		return issuer.IssueResponse{}, err
	}

	// if there is an existing order, we check to make sure it is up to date
	// with the current certificate & issuer configuration.
	// if it is not, we will abandon the old order and create a new one.
	// The 'retry' cases here will bypass the controller's rate-limiting, as
	// well as the back-off applied to failing ACME Orders.
	// They should therefore *only* match on changes to the actual Certificate
	// resource, or underlying Order (i.e. user interaction).
	if existingOrder != nil {
		glog.V(4).Infof("Validating existing order CSR for Certificate %s/%s", crt.Namespace, crt.Name)

		// if the existing order has expired, we should create a new one
		// TODO: implement this order state in the acmeorders controller
		if existingOrder.Status.State == v1alpha1.Expired {
			return a.retryOrder(existingOrder)
		}

		// check if the existing order has failed.
		// If it has, we check to see when it last failed and 'back-off' if it
		// failed in the recent past.
		if acme.IsFailureState(existingOrder.Status.State) {
			if crt.Status.LastFailureTime == nil {
				nowTime := metav1.NewTime(a.clock.Now())
				crt.Status.LastFailureTime = &nowTime
			}

			if time.Now().Sub(crt.Status.LastFailureTime.Time) < createOrderWaitDuration {
				return issuer.IssueResponse{}, fmt.Errorf("applying acme order back-off for certificate %s/%s because it has failed within the last %s", crt.Namespace, crt.Name, createOrderWaitDuration)
			}

			// otherwise, we clear the lastFailureTime and create a new order
			// as the back-off time has passed.
			crt.Status.LastFailureTime = nil

			return a.retryOrder(existingOrder)
		}

		// check the CSR is created by the private key that we hold
		csrBytes := existingOrder.Spec.CSR
		if len(csrBytes) == 0 {
			return a.retryOrder(existingOrder)
		}
		existingCSR, err := x509.ParseCertificateRequest(csrBytes)
		if err != nil {
			return a.retryOrder(existingOrder)
		}

		matches, err := pki.PublicKeyMatchesCSR(key.Public(), existingCSR)
		if err != nil {
			return issuer.IssueResponse{}, err
		}

		if !matches {
			glog.V(4).Infof("CSR on existing order resource does not match certificate %s/%s private key. Creating new order.", crt.Namespace, crt.Name)
			return a.retryOrder(existingOrder)
		}
	}

	if existingOrder == nil {
		glog.V(4).Infof("Creating new Order resource for Certificate %s/%s", crt.Namespace, crt.Name)

		csr, err := pki.GenerateCSR(a.issuer, crt)
		if err != nil {
			// TODO: what errors can be produced here? some error types might
			// be permenant, and we should handle that properly.
			return issuer.IssueResponse{}, err
		}

		csrBytes, err := pki.EncodeCSR(csr, key)
		if err != nil {
			return issuer.IssueResponse{}, err
		}

		// set the CSR field on the order to be created
		expectedOrder.Spec.CSR = csrBytes

		existingOrder, err = a.CMClient.CertmanagerV1alpha1().Orders(crt.Namespace).Create(expectedOrder)
		if err != nil {
			return issuer.IssueResponse{}, err
		}

		glog.V(4).Infof("Created new Order resource named %q for Certificate %s/%s", expectedOrder.Name, crt.Namespace, crt.Name)

		return issuer.IssueResponse{}, nil
	}

	if existingOrder.Status.State != v1alpha1.Valid {
		glog.Infof("Order %s/%s is not in 'valid' state. Waiting for Order to transition before attempting to issue Certificate.", existingOrder.Namespace, existingOrder.Name)

		// We don't immediately requeue, as the change to the Order resource on
		// transition should trigger the certificate to be re-synced.
		return issuer.IssueResponse{}, nil
	}

	// If the order is valid, we can attempt to retrieve the Certificate.
	// First obtain an ACME client to make this easier.
	cl, err := a.helper.ClientForIssuer(a.issuer)
	if err != nil {
		return issuer.IssueResponse{}, err
	}

	certSlice, err := cl.GetCertificate(ctx, existingOrder.Status.CertificateURL)
	if err != nil {
		// TODO: parse returned ACME error and potentially re-create order.
		return issuer.IssueResponse{}, err
	}

	if len(certSlice) == 0 {
		// TODO: parse returned ACME error and potentially re-create order.
		return issuer.IssueResponse{}, fmt.Errorf("invalid certificate returned from acme server")
	}

	x509Cert, err := x509.ParseCertificate(certSlice[0])
	if err != nil {
		// TODO: parse returned ACME error and potentially re-create order.
		return issuer.IssueResponse{}, fmt.Errorf("failed to parse returned x509 certificate: %v", err.Error())
	}

	if a.Context.IssuerOptions.CertificateNeedsRenew(x509Cert) {
		// existing order's certificate is near expiry
		return a.retryOrder(existingOrder)
	}

	// encode the retrieved certificates (including the chain)
	certBuffer := bytes.NewBuffer([]byte{})
	for _, cert := range certSlice {
		err := pem.Encode(certBuffer, &pem.Block{Type: "CERTIFICATE", Bytes: cert})
		if err != nil {
			return issuer.IssueResponse{}, err
		}
	}

	// encode the private key and return
	keyPem, err := pki.EncodePrivateKey(key)
	if err != nil {
		// TODO: this is probably an internal error - we should fail safer here
		return issuer.IssueResponse{}, err
	}

	return issuer.IssueResponse{
		Certificate: certBuffer.Bytes(),
		PrivateKey:  keyPem,
	}, nil
}

func (a *Acme) cleanupOwnedOrders(crt *v1alpha1.Certificate, retain string) error {
	labelMap := certLabels(crt.Name)
	selector := labels.NewSelector()
	for k, v := range labelMap {
		req, err := labels.NewRequirement(k, selection.Equals, []string{v})
		if err != nil {
			return err
		}
		selector.Add(*req)
	}
	existingOrders, err := a.orderLister.Orders(crt.Namespace).List(selector)
	if err != nil {
		return err
	}

	var errs []error
	for _, o := range existingOrders {
		// Don't touch any objects that don't have this certificate set as the
		// owner reference.
		if !metav1.IsControlledBy(o, crt) {
			continue
		}

		if o.Name == retain {
			glog.V(4).Infof("Skipping cleanup for active order resource %q", retain)
			continue
		}

		// delete any old order resources
		glog.Infof("Deleting Order resource %s/%s", o.Namespace, o.Name)
		a.Recorder.Eventf(crt, corev1.EventTypeNormal, "Cleanup",
			fmt.Sprintf("Deleting old Order resource %q", o.Name))

		err := a.CMClient.CertmanagerV1alpha1().Orders(o.Namespace).Delete(o.Name, nil)
		if err != nil && !apierrors.IsNotFound(err) {
			glog.Errorf("Error deleting Order resource %s/%s: %v", o.Namespace, o.Name, err)
			errs = append(errs, err)
			continue
		}
	}

	return utilerrors.NewAggregate(errs)
}

func (a *Acme) getCertificatePrivateKey(crt *v1alpha1.Certificate) (crypto.Signer, bool, error) {
	glog.V(4).Infof("Attempting to fetch existing certificate private key")

	// If a private key already exists, reuse it.
	// TODO: if we have not observed the update to the Secret resource with the
	// private key yet, we may in some cases loop and re-generate the private key
	// over and over. We could attempt to use the live clientset to read the
	// private key too to avoid this case.
	key, err := kube.SecretTLSKey(a.secretsLister, crt.Namespace, crt.Spec.SecretName)
	if err == nil {
		return key, false, nil
	}

	// We only generate a new private key if the existing one is not found or
	// contains invalid data.
	// TODO: should we re-generate on InvalidData?
	if !apierrors.IsNotFound(err) && !errors.IsInvalidData(err) {
		return nil, false, err
	}

	glog.V(4).Infof("Generating new private key for %s/%s", crt.Namespace, crt.Name)

	// generate a new private key.
	rsaKey, err := pki.GenerateRSAPrivateKey(2048)
	if err != nil {
		return nil, false, err
	}

	return rsaKey, true, nil
}

// retryOrder will delete the existing order with the foreground
// deletion policy.
// If delete successfully (i.e. cleaned up), the order name will be
// reset to empty and a resync of the resource will begin.
func (a *Acme) retryOrder(existingOrder *v1alpha1.Order) (issuer.IssueResponse, error) {
	foregroundDeletion := metav1.DeletePropagationForeground
	err := a.CMClient.CertmanagerV1alpha1().Orders(existingOrder.Namespace).Delete(existingOrder.Name, &metav1.DeleteOptions{
		PropagationPolicy: &foregroundDeletion,
	})
	if err != nil {
		return issuer.IssueResponse{}, err
	}

	// Updating the certificate status will trigger a requeue once the change
	// has been observed by the informer.
	// If we set Requeue: true here, we may cause a race where the lister has
	// not observed the updated orderRef.
	return issuer.IssueResponse{}, nil
}

func buildOrder(crt *v1alpha1.Certificate, csr []byte) (*v1alpha1.Order, error) {
	spec := v1alpha1.OrderSpec{
		CSR:        csr,
		IssuerRef:  crt.Spec.IssuerRef,
		CommonName: crt.Spec.CommonName,
		DNSNames:   crt.Spec.DNSNames,
		Config:     crt.Spec.ACME.Config,
	}
	hash, err := hashOrder(spec)
	if err != nil {
		return nil, err
	}

	return &v1alpha1.Order{
		ObjectMeta: metav1.ObjectMeta{
			Name:            fmt.Sprintf("%s-%d", crt.Name, hash),
			Namespace:       crt.Namespace,
			Labels:          certLabels(crt.Name),
			OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(crt, certificateGvk)},
		},
		Spec: spec,
	}, nil
}

func certLabels(crtName string) map[string]string {
	return map[string]string{
		"acme.cert-manager.io/certificate-name": crtName,
	}
}

func hashOrder(orderSpec v1alpha1.OrderSpec) (uint32, error) {
	// create a shallow copy of the OrderSpec so we can overwrite the CSR field
	orderSpec.CSR = nil

	orderSpecBytes, err := json.Marshal(orderSpec)
	if err != nil {
		return 0, err
	}

	hashF := fnv.New32()
	_, err = hashF.Write(orderSpecBytes)
	if err != nil {
		return 0, err
	}

	return hashF.Sum32(), nil
}
<commit_msg>Reorder checking for existingOrder<commit_after>/*
Copyright 2018 The Jetstack cert-manager contributors.

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 acme

import (
	"bytes"
	"context"
	"crypto"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"fmt"
	"hash/fnv"
	"time"

	"github.com/golang/glog"
	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/selection"
	utilerrors "k8s.io/apimachinery/pkg/util/errors"

	"github.com/jetstack/cert-manager/pkg/acme"
	"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
	"github.com/jetstack/cert-manager/pkg/issuer"
	"github.com/jetstack/cert-manager/pkg/util/errors"
	"github.com/jetstack/cert-manager/pkg/util/kube"
	"github.com/jetstack/cert-manager/pkg/util/pki"
)

const (
	createOrderWaitDuration = time.Minute * 5
)

var (
	certificateGvk = v1alpha1.SchemeGroupVersion.WithKind("Certificate")
)

func (a *Acme) Issue(ctx context.Context, crt *v1alpha1.Certificate) (issuer.IssueResponse, error) {
	key, generated, err := a.getCertificatePrivateKey(crt)
	if err != nil {
		glog.Errorf("Error getting certificate private key: %v", err)
		return issuer.IssueResponse{}, err
	}
	if generated {
		// If we have generated a new private key, we return here to ensure we
		// successfully persist the key before creating any CSRs with it.
		glog.V(4).Infof("Storing new certificate private key for %s/%s", crt.Namespace, crt.Name)

		keyPem, err := pki.EncodePrivateKey(key)
		if err != nil {
			return issuer.IssueResponse{}, err
		}

		return issuer.IssueResponse{
			PrivateKey: keyPem,
		}, nil
	}

	// Initially, we do not set the csr on the order resource we build.
	// This is to save having the overhead of generating a new CSR in the case
	// where the Order resource is up to date already, and also because we have
	// not actually read the existing certificate private key yet to ensure it
	// exists.
	expectedOrder, err := buildOrder(crt, nil)
	if err != nil {
		return issuer.IssueResponse{}, err
	}

	// Cleanup Order resources that are owned by this Certificate but are not
	// up to date (i.e. do not match the requirements on the Certificate).
	// Because the order name returned by buildOrder is a hash of its spec, we
	// can simply delete all order resources that are owned by us that do not
	// have the same name.
	err = a.cleanupOwnedOrders(crt, expectedOrder.Name)
	if err != nil {
		glog.Errorf("Error cleaning up old orders: %v", err)
		return issuer.IssueResponse{}, err
	}

	// Obtain the existing Order for this Certificate from the API server.
	// If it does not exist, we continue on as it will be created from
	// the generated expectedOrder.
	existingOrder, err := a.orderLister.Orders(expectedOrder.Namespace).Get(expectedOrder.Name)
	if err != nil && !apierrors.IsNotFound(err) {
		glog.Errorf("Error getting existing Order resource: %v", err)
		return issuer.IssueResponse{}, err
	}
	if existingOrder == nil {
		return issuer.IssueResponse{}, a.createNewOrder(crt, expectedOrder, key)
	}

	// if there is an existing order, we check to make sure it is up to date
	// with the current certificate & issuer configuration.
	// if it is not, we will abandon the old order and create a new one.
	// The 'retry' cases here will bypass the controller's rate-limiting, as
	// well as the back-off applied to failing ACME Orders.
	// They should therefore *only* match on changes to the actual Certificate
	// resource, or underlying Order (i.e. user interaction).
	glog.V(4).Infof("Validating existing order CSR for Certificate %s/%s", crt.Namespace, crt.Name)

	// if the existing order has expired, we should create a new one
	// TODO: implement this order state in the acmeorders controller
	if existingOrder.Status.State == v1alpha1.Expired {
		return a.retryOrder(existingOrder)
	}

	// check if the existing order has failed.
	// If it has, we check to see when it last failed and 'back-off' if it
	// failed in the recent past.
	if acme.IsFailureState(existingOrder.Status.State) {
		if crt.Status.LastFailureTime == nil {
			nowTime := metav1.NewTime(a.clock.Now())
			crt.Status.LastFailureTime = &nowTime
		}

		if time.Now().Sub(crt.Status.LastFailureTime.Time) < createOrderWaitDuration {
			return issuer.IssueResponse{}, fmt.Errorf("applying acme order back-off for certificate %s/%s because it has failed within the last %s", crt.Namespace, crt.Name, createOrderWaitDuration)
		}

		// otherwise, we clear the lastFailureTime and create a new order
		// as the back-off time has passed.
		crt.Status.LastFailureTime = nil

		return a.retryOrder(existingOrder)
	}

	// check the CSR is created by the private key that we hold
	csrBytes := existingOrder.Spec.CSR
	if len(csrBytes) == 0 {
		return a.retryOrder(existingOrder)
	}
	existingCSR, err := x509.ParseCertificateRequest(csrBytes)
	if err != nil {
		return a.retryOrder(existingOrder)
	}

	matches, err := pki.PublicKeyMatchesCSR(key.Public(), existingCSR)
	if err != nil {
		return issuer.IssueResponse{}, err
	}

	if !matches {
		glog.V(4).Infof("CSR on existing order resource does not match certificate %s/%s private key. Creating new order.", crt.Namespace, crt.Name)
		return a.retryOrder(existingOrder)
	}

	if existingOrder.Status.State != v1alpha1.Valid {
		glog.Infof("Order %s/%s is not in 'valid' state. Waiting for Order to transition before attempting to issue Certificate.", existingOrder.Namespace, existingOrder.Name)

		// We don't immediately requeue, as the change to the Order resource on
		// transition should trigger the certificate to be re-synced.
		return issuer.IssueResponse{}, nil
	}

	// If the order is valid, we can attempt to retrieve the Certificate.
	// First obtain an ACME client to make this easier.
	cl, err := a.helper.ClientForIssuer(a.issuer)
	if err != nil {
		return issuer.IssueResponse{}, err
	}

	certSlice, err := cl.GetCertificate(ctx, existingOrder.Status.CertificateURL)
	if err != nil {
		// TODO: parse returned ACME error and potentially re-create order.
		return issuer.IssueResponse{}, err
	}

	if len(certSlice) == 0 {
		// TODO: parse returned ACME error and potentially re-create order.
		return issuer.IssueResponse{}, fmt.Errorf("invalid certificate returned from acme server")
	}

	x509Cert, err := x509.ParseCertificate(certSlice[0])
	if err != nil {
		// TODO: parse returned ACME error and potentially re-create order.
		return issuer.IssueResponse{}, fmt.Errorf("failed to parse returned x509 certificate: %v", err.Error())
	}

	if a.Context.IssuerOptions.CertificateNeedsRenew(x509Cert) {
		// existing order's certificate is near expiry
		return a.retryOrder(existingOrder)
	}

	// encode the retrieved certificates (including the chain)
	certBuffer := bytes.NewBuffer([]byte{})
	for _, cert := range certSlice {
		err := pem.Encode(certBuffer, &pem.Block{Type: "CERTIFICATE", Bytes: cert})
		if err != nil {
			return issuer.IssueResponse{}, err
		}
	}

	// encode the private key and return
	keyPem, err := pki.EncodePrivateKey(key)
	if err != nil {
		// TODO: this is probably an internal error - we should fail safer here
		return issuer.IssueResponse{}, err
	}

	return issuer.IssueResponse{
		Certificate: certBuffer.Bytes(),
		PrivateKey:  keyPem,
	}, nil
}

func (a *Acme) cleanupOwnedOrders(crt *v1alpha1.Certificate, retain string) error {
	labelMap := certLabels(crt.Name)
	selector := labels.NewSelector()
	for k, v := range labelMap {
		req, err := labels.NewRequirement(k, selection.Equals, []string{v})
		if err != nil {
			return err
		}
		selector.Add(*req)
	}
	existingOrders, err := a.orderLister.Orders(crt.Namespace).List(selector)
	if err != nil {
		return err
	}

	var errs []error
	for _, o := range existingOrders {
		// Don't touch any objects that don't have this certificate set as the
		// owner reference.
		if !metav1.IsControlledBy(o, crt) {
			continue
		}

		if o.Name == retain {
			glog.V(4).Infof("Skipping cleanup for active order resource %q", retain)
			continue
		}

		// delete any old order resources
		glog.Infof("Deleting Order resource %s/%s", o.Namespace, o.Name)
		a.Recorder.Eventf(crt, corev1.EventTypeNormal, "Cleanup",
			fmt.Sprintf("Deleting old Order resource %q", o.Name))

		err := a.CMClient.CertmanagerV1alpha1().Orders(o.Namespace).Delete(o.Name, nil)
		if err != nil && !apierrors.IsNotFound(err) {
			glog.Errorf("Error deleting Order resource %s/%s: %v", o.Namespace, o.Name, err)
			errs = append(errs, err)
			continue
		}
	}

	return utilerrors.NewAggregate(errs)
}

func (a *Acme) getCertificatePrivateKey(crt *v1alpha1.Certificate) (crypto.Signer, bool, error) {
	glog.V(4).Infof("Attempting to fetch existing certificate private key")

	// If a private key already exists, reuse it.
	// TODO: if we have not observed the update to the Secret resource with the
	// private key yet, we may in some cases loop and re-generate the private key
	// over and over. We could attempt to use the live clientset to read the
	// private key too to avoid this case.
	key, err := kube.SecretTLSKey(a.secretsLister, crt.Namespace, crt.Spec.SecretName)
	if err == nil {
		return key, false, nil
	}

	// We only generate a new private key if the existing one is not found or
	// contains invalid data.
	// TODO: should we re-generate on InvalidData?
	if !apierrors.IsNotFound(err) && !errors.IsInvalidData(err) {
		return nil, false, err
	}

	glog.V(4).Infof("Generating new private key for %s/%s", crt.Namespace, crt.Name)

	// generate a new private key.
	rsaKey, err := pki.GenerateRSAPrivateKey(2048)
	if err != nil {
		return nil, false, err
	}

	return rsaKey, true, nil
}

func (a *Acme) createNewOrder(crt *v1alpha1.Certificate, template *v1alpha1.Order, key crypto.Signer) error {
	glog.V(4).Infof("Creating new Order resource for Certificate %s/%s", crt.Namespace, crt.Name)

	csr, err := pki.GenerateCSR(a.issuer, crt)
	if err != nil {
		// TODO: what errors can be produced here? some error types might
		// be permenant, and we should handle that properly.
		return err
	}

	csrBytes, err := pki.EncodeCSR(csr, key)
	if err != nil {
		return err
	}

	// set the CSR field on the order to be created
	template.Spec.CSR = csrBytes

	_, err = a.CMClient.CertmanagerV1alpha1().Orders(template.Namespace).Create(template)
	if err != nil {
		return err
	}

	glog.V(4).Infof("Created new Order resource named %q for Certificate %s/%s", template.Name, crt.Namespace, crt.Name)

	return nil
}

// retryOrder will delete the existing order with the foreground
// deletion policy.
// If delete successfully (i.e. cleaned up), the order name will be
// reset to empty and a resync of the resource will begin.
func (a *Acme) retryOrder(existingOrder *v1alpha1.Order) (issuer.IssueResponse, error) {
	foregroundDeletion := metav1.DeletePropagationForeground
	err := a.CMClient.CertmanagerV1alpha1().Orders(existingOrder.Namespace).Delete(existingOrder.Name, &metav1.DeleteOptions{
		PropagationPolicy: &foregroundDeletion,
	})
	if err != nil {
		return issuer.IssueResponse{}, err
	}

	// Updating the certificate status will trigger a requeue once the change
	// has been observed by the informer.
	// If we set Requeue: true here, we may cause a race where the lister has
	// not observed the updated orderRef.
	return issuer.IssueResponse{}, nil
}

func buildOrder(crt *v1alpha1.Certificate, csr []byte) (*v1alpha1.Order, error) {
	spec := v1alpha1.OrderSpec{
		CSR:        csr,
		IssuerRef:  crt.Spec.IssuerRef,
		CommonName: crt.Spec.CommonName,
		DNSNames:   crt.Spec.DNSNames,
		Config:     crt.Spec.ACME.Config,
	}
	hash, err := hashOrder(spec)
	if err != nil {
		return nil, err
	}

	return &v1alpha1.Order{
		ObjectMeta: metav1.ObjectMeta{
			Name:            fmt.Sprintf("%s-%d", crt.Name, hash),
			Namespace:       crt.Namespace,
			Labels:          certLabels(crt.Name),
			OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(crt, certificateGvk)},
		},
		Spec: spec,
	}, nil
}

func certLabels(crtName string) map[string]string {
	return map[string]string{
		"acme.cert-manager.io/certificate-name": crtName,
	}
}

func hashOrder(orderSpec v1alpha1.OrderSpec) (uint32, error) {
	// create a shallow copy of the OrderSpec so we can overwrite the CSR field
	orderSpec.CSR = nil

	orderSpecBytes, err := json.Marshal(orderSpec)
	if err != nil {
		return 0, err
	}

	hashF := fnv.New32()
	_, err = hashF.Write(orderSpecBytes)
	if err != nil {
		return 0, err
	}

	return hashF.Sum32(), nil
}
<|endoftext|>


<commit_before>// +build windows

package term

import (
	"io"
	"os"
	"os/signal"
	"syscall"

	"github.com/Azure/go-ansiterm/winterm"
	"github.com/docker/docker/pkg/system"
	"github.com/docker/docker/pkg/term/windows"
)

// State holds the console mode for the terminal.
type State struct {
	inMode, outMode     uint32
	inHandle, outHandle syscall.Handle
}

// Winsize is used for window size.
type Winsize struct {
	Height uint16
	Width  uint16
}

const (
	// https://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx
	enableVirtualTerminalInput      = 0x0200
	enableVirtualTerminalProcessing = 0x0004
	disableNewlineAutoReturn        = 0x0008
)

// usingNativeConsole is true if we are using the Windows native console
var usingNativeConsole bool

// StdStreams returns the standard streams (stdin, stdout, stedrr).
func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
	switch {
	case os.Getenv("ConEmuANSI") == "ON":
		// The ConEmu terminal emulates ANSI on output streams well.
		return windows.ConEmuStreams()
	case os.Getenv("MSYSTEM") != "":
		// MSYS (mingw) does not emulate ANSI well.
		return windows.ConsoleStreams()
	default:
		if useNativeConsole() {
			usingNativeConsole = true
			return os.Stdin, os.Stdout, os.Stderr
		}
		return windows.ConsoleStreams()
	}
}

// useNativeConsole determines if the docker client should use the built-in
// console which supports ANSI emulation, or fall-back to the golang emulator
// (github.com/azure/go-ansiterm).
func useNativeConsole() bool {
	osv := system.GetOSVersion()

	// Native console is not available before major version 10
	if osv.MajorVersion < 10 {
		return false
	}

	// Get the console modes. If this fails, we can't use the native console
	state, err := getNativeConsole()
	if err != nil {
		return false
	}

	// Probe the console to see if it can be enabled.
	if nil != probeNativeConsole(state) {
		return false
	}

	// Environment variable override
	if e := os.Getenv("USE_NATIVE_CONSOLE"); e != "" {
		if e == "1" {
			return true
		}
		return false
	}

	// TODO Windows. The native emulator still has issues which
	// mean it shouldn't be enabled for everyone. Change this next line to true
	// to change the default to "enable if available". In the meantime, users
	// can still try it out by using USE_NATIVE_CONSOLE env variable.
	return false
}

// getNativeConsole returns the console modes ('state') for the native Windows console
func getNativeConsole() (State, error) {
	var (
		err   error
		state State
	)

	// Get the handle to stdout
	if state.outHandle, err = syscall.GetStdHandle(syscall.STD_OUTPUT_HANDLE); err != nil {
		return state, err
	}

	// Get the console mode from the consoles stdout handle
	if err = syscall.GetConsoleMode(state.outHandle, &state.outMode); err != nil {
		return state, err
	}

	// Get the handle to stdin
	if state.inHandle, err = syscall.GetStdHandle(syscall.STD_INPUT_HANDLE); err != nil {
		return state, err
	}

	// Get the console mode from the consoles stdin handle
	if err = syscall.GetConsoleMode(state.inHandle, &state.inMode); err != nil {
		return state, err
	}

	return state, nil
}

// probeNativeConsole probes the console to determine if native can be supported,
func probeNativeConsole(state State) error {
	if err := winterm.SetConsoleMode(uintptr(state.outHandle), state.outMode|enableVirtualTerminalProcessing); err != nil {
		return err
	}
	defer winterm.SetConsoleMode(uintptr(state.outHandle), state.outMode)

	if err := winterm.SetConsoleMode(uintptr(state.inHandle), state.inMode|enableVirtualTerminalInput); err != nil {
		return err
	}
	defer winterm.SetConsoleMode(uintptr(state.inHandle), state.inMode)

	return nil
}

// enableNativeConsole turns on native console mode
func enableNativeConsole(state State) error {
	// First attempt both enableVirtualTerminalProcessing and disableNewlineAutoReturn
	if err := winterm.SetConsoleMode(uintptr(state.outHandle),
		state.outMode|(enableVirtualTerminalProcessing|disableNewlineAutoReturn)); err != nil {

		// That may fail, so fallback to trying just enableVirtualTerminalProcessing
		if err := winterm.SetConsoleMode(uintptr(state.outHandle), state.outMode|enableVirtualTerminalProcessing); err != nil {
			return err
		}
	}

	if err := winterm.SetConsoleMode(uintptr(state.inHandle), state.inMode|enableVirtualTerminalInput); err != nil {
		winterm.SetConsoleMode(uintptr(state.outHandle), state.outMode) // restore out if we can
		return err
	}

	return nil
}

// disableNativeConsole turns off native console mode
func disableNativeConsole(state *State) error {
	// Try and restore both in an out before error checking.
	errout := winterm.SetConsoleMode(uintptr(state.outHandle), state.outMode)
	errin := winterm.SetConsoleMode(uintptr(state.inHandle), state.inMode)
	if errout != nil {
		return errout
	}
	if errin != nil {
		return errin
	}
	return nil
}

// GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal.
func GetFdInfo(in interface{}) (uintptr, bool) {
	return windows.GetHandleInfo(in)
}

// GetWinsize returns the window size based on the specified file descriptor.
func GetWinsize(fd uintptr) (*Winsize, error) {
	info, err := winterm.GetConsoleScreenBufferInfo(fd)
	if err != nil {
		return nil, err
	}

	winsize := &Winsize{
		Width:  uint16(info.Window.Right - info.Window.Left + 1),
		Height: uint16(info.Window.Bottom - info.Window.Top + 1),
	}

	return winsize, nil
}

// IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal(fd uintptr) bool {
	return windows.IsConsole(fd)
}

// RestoreTerminal restores the terminal connected to the given file descriptor
// to a previous state.
func RestoreTerminal(fd uintptr, state *State) error {
	if usingNativeConsole {
		return disableNativeConsole(state)
	}
	return winterm.SetConsoleMode(fd, state.outMode)
}

// SaveState saves the state of the terminal connected to the given file descriptor.
func SaveState(fd uintptr) (*State, error) {
	if usingNativeConsole {
		state, err := getNativeConsole()
		if err != nil {
			return nil, err
		}
		return &state, nil
	}

	mode, e := winterm.GetConsoleMode(fd)
	if e != nil {
		return nil, e
	}

	return &State{outMode: mode}, nil
}

// DisableEcho disables echo for the terminal connected to the given file descriptor.
// -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
func DisableEcho(fd uintptr, state *State) error {
	mode := state.inMode
	mode &^= winterm.ENABLE_ECHO_INPUT
	mode |= winterm.ENABLE_PROCESSED_INPUT | winterm.ENABLE_LINE_INPUT
	err := winterm.SetConsoleMode(fd, mode)
	if err != nil {
		return err
	}

	// Register an interrupt handler to catch and restore prior state
	restoreAtInterrupt(fd, state)
	return nil
}

// SetRawTerminal puts the terminal connected to the given file descriptor into raw
// mode and returns the previous state.
func SetRawTerminal(fd uintptr) (*State, error) {
	state, err := MakeRaw(fd)
	if err != nil {
		return nil, err
	}

	// Register an interrupt handler to catch and restore prior state
	restoreAtInterrupt(fd, state)
	return state, err
}

// MakeRaw puts the terminal (Windows Console) connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be restored.
func MakeRaw(fd uintptr) (*State, error) {
	state, err := SaveState(fd)
	if err != nil {
		return nil, err
	}

	mode := state.inMode
	if usingNativeConsole {
		if err := enableNativeConsole(*state); err != nil {
			return nil, err
		}
		mode |= enableVirtualTerminalInput
	}

	// See
	// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx
	// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx

	// Disable these modes
	mode &^= winterm.ENABLE_ECHO_INPUT
	mode &^= winterm.ENABLE_LINE_INPUT
	mode &^= winterm.ENABLE_MOUSE_INPUT
	mode &^= winterm.ENABLE_WINDOW_INPUT
	mode &^= winterm.ENABLE_PROCESSED_INPUT

	// Enable these modes
	mode |= winterm.ENABLE_EXTENDED_FLAGS
	mode |= winterm.ENABLE_INSERT_MODE
	mode |= winterm.ENABLE_QUICK_EDIT_MODE

	err = winterm.SetConsoleMode(fd, mode)
	if err != nil {
		return nil, err
	}
	return state, nil
}

func restoreAtInterrupt(fd uintptr, state *State) {
	sigchan := make(chan os.Signal, 1)
	signal.Notify(sigchan, os.Interrupt)

	go func() {
		_ = <-sigchan
		RestoreTerminal(fd, state)
		os.Exit(0)
	}()
}
<commit_msg>Windows: Turn on native console by default<commit_after>// +build windows

package term

import (
	"io"
	"os"
	"os/signal"
	"syscall"

	"github.com/Azure/go-ansiterm/winterm"
	"github.com/docker/docker/pkg/system"
	"github.com/docker/docker/pkg/term/windows"
)

// State holds the console mode for the terminal.
type State struct {
	inMode, outMode     uint32
	inHandle, outHandle syscall.Handle
}

// Winsize is used for window size.
type Winsize struct {
	Height uint16
	Width  uint16
}

const (
	// https://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx
	enableVirtualTerminalInput      = 0x0200
	enableVirtualTerminalProcessing = 0x0004
	disableNewlineAutoReturn        = 0x0008
)

// usingNativeConsole is true if we are using the Windows native console
var usingNativeConsole bool

// StdStreams returns the standard streams (stdin, stdout, stedrr).
func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
	switch {
	case os.Getenv("ConEmuANSI") == "ON":
		// The ConEmu terminal emulates ANSI on output streams well.
		return windows.ConEmuStreams()
	case os.Getenv("MSYSTEM") != "":
		// MSYS (mingw) does not emulate ANSI well.
		return windows.ConsoleStreams()
	default:
		if useNativeConsole() {
			usingNativeConsole = true
			return os.Stdin, os.Stdout, os.Stderr
		}
		return windows.ConsoleStreams()
	}
}

// useNativeConsole determines if the docker client should use the built-in
// console which supports ANSI emulation, or fall-back to the golang emulator
// (github.com/azure/go-ansiterm).
func useNativeConsole() bool {
	osv := system.GetOSVersion()

	// Native console is not available before major version 10
	if osv.MajorVersion < 10 {
		return false
	}

	// Get the console modes. If this fails, we can't use the native console
	state, err := getNativeConsole()
	if err != nil {
		return false
	}

	// Probe the console to see if it can be enabled.
	if nil != probeNativeConsole(state) {
		return false
	}

	// Environment variable override
	if e := os.Getenv("USE_NATIVE_CONSOLE"); e != "" {
		if e == "1" {
			return true
		}
		return false
	}

	// Must have a post-TP5 RS1 build of Windows Server 2016/Windows 10 for
	// the native console to be usable.
	if osv.Build < 14350 {
		return false
	}

	return true
}

// getNativeConsole returns the console modes ('state') for the native Windows console
func getNativeConsole() (State, error) {
	var (
		err   error
		state State
	)

	// Get the handle to stdout
	if state.outHandle, err = syscall.GetStdHandle(syscall.STD_OUTPUT_HANDLE); err != nil {
		return state, err
	}

	// Get the console mode from the consoles stdout handle
	if err = syscall.GetConsoleMode(state.outHandle, &state.outMode); err != nil {
		return state, err
	}

	// Get the handle to stdin
	if state.inHandle, err = syscall.GetStdHandle(syscall.STD_INPUT_HANDLE); err != nil {
		return state, err
	}

	// Get the console mode from the consoles stdin handle
	if err = syscall.GetConsoleMode(state.inHandle, &state.inMode); err != nil {
		return state, err
	}

	return state, nil
}

// probeNativeConsole probes the console to determine if native can be supported,
func probeNativeConsole(state State) error {
	if err := winterm.SetConsoleMode(uintptr(state.outHandle), state.outMode|enableVirtualTerminalProcessing); err != nil {
		return err
	}
	defer winterm.SetConsoleMode(uintptr(state.outHandle), state.outMode)

	if err := winterm.SetConsoleMode(uintptr(state.inHandle), state.inMode|enableVirtualTerminalInput); err != nil {
		return err
	}
	defer winterm.SetConsoleMode(uintptr(state.inHandle), state.inMode)

	return nil
}

// enableNativeConsole turns on native console mode
func enableNativeConsole(state State) error {
	// First attempt both enableVirtualTerminalProcessing and disableNewlineAutoReturn
	if err := winterm.SetConsoleMode(uintptr(state.outHandle),
		state.outMode|(enableVirtualTerminalProcessing|disableNewlineAutoReturn)); err != nil {

		// That may fail, so fallback to trying just enableVirtualTerminalProcessing
		if err := winterm.SetConsoleMode(uintptr(state.outHandle), state.outMode|enableVirtualTerminalProcessing); err != nil {
			return err
		}
	}

	if err := winterm.SetConsoleMode(uintptr(state.inHandle), state.inMode|enableVirtualTerminalInput); err != nil {
		winterm.SetConsoleMode(uintptr(state.outHandle), state.outMode) // restore out if we can
		return err
	}

	return nil
}

// disableNativeConsole turns off native console mode
func disableNativeConsole(state *State) error {
	// Try and restore both in an out before error checking.
	errout := winterm.SetConsoleMode(uintptr(state.outHandle), state.outMode)
	errin := winterm.SetConsoleMode(uintptr(state.inHandle), state.inMode)
	if errout != nil {
		return errout
	}
	if errin != nil {
		return errin
	}
	return nil
}

// GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal.
func GetFdInfo(in interface{}) (uintptr, bool) {
	return windows.GetHandleInfo(in)
}

// GetWinsize returns the window size based on the specified file descriptor.
func GetWinsize(fd uintptr) (*Winsize, error) {
	info, err := winterm.GetConsoleScreenBufferInfo(fd)
	if err != nil {
		return nil, err
	}

	winsize := &Winsize{
		Width:  uint16(info.Window.Right - info.Window.Left + 1),
		Height: uint16(info.Window.Bottom - info.Window.Top + 1),
	}

	return winsize, nil
}

// IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal(fd uintptr) bool {
	return windows.IsConsole(fd)
}

// RestoreTerminal restores the terminal connected to the given file descriptor
// to a previous state.
func RestoreTerminal(fd uintptr, state *State) error {
	if usingNativeConsole {
		return disableNativeConsole(state)
	}
	return winterm.SetConsoleMode(fd, state.outMode)
}

// SaveState saves the state of the terminal connected to the given file descriptor.
func SaveState(fd uintptr) (*State, error) {
	if usingNativeConsole {
		state, err := getNativeConsole()
		if err != nil {
			return nil, err
		}
		return &state, nil
	}

	mode, e := winterm.GetConsoleMode(fd)
	if e != nil {
		return nil, e
	}

	return &State{outMode: mode}, nil
}

// DisableEcho disables echo for the terminal connected to the given file descriptor.
// -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
func DisableEcho(fd uintptr, state *State) error {
	mode := state.inMode
	mode &^= winterm.ENABLE_ECHO_INPUT
	mode |= winterm.ENABLE_PROCESSED_INPUT | winterm.ENABLE_LINE_INPUT
	err := winterm.SetConsoleMode(fd, mode)
	if err != nil {
		return err
	}

	// Register an interrupt handler to catch and restore prior state
	restoreAtInterrupt(fd, state)
	return nil
}

// SetRawTerminal puts the terminal connected to the given file descriptor into raw
// mode and returns the previous state.
func SetRawTerminal(fd uintptr) (*State, error) {
	state, err := MakeRaw(fd)
	if err != nil {
		return nil, err
	}

	// Register an interrupt handler to catch and restore prior state
	restoreAtInterrupt(fd, state)
	return state, err
}

// MakeRaw puts the terminal (Windows Console) connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be restored.
func MakeRaw(fd uintptr) (*State, error) {
	state, err := SaveState(fd)
	if err != nil {
		return nil, err
	}

	mode := state.inMode
	if usingNativeConsole {
		if err := enableNativeConsole(*state); err != nil {
			return nil, err
		}
		mode |= enableVirtualTerminalInput
	}

	// See
	// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx
	// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx

	// Disable these modes
	mode &^= winterm.ENABLE_ECHO_INPUT
	mode &^= winterm.ENABLE_LINE_INPUT
	mode &^= winterm.ENABLE_MOUSE_INPUT
	mode &^= winterm.ENABLE_WINDOW_INPUT
	mode &^= winterm.ENABLE_PROCESSED_INPUT

	// Enable these modes
	mode |= winterm.ENABLE_EXTENDED_FLAGS
	mode |= winterm.ENABLE_INSERT_MODE
	mode |= winterm.ENABLE_QUICK_EDIT_MODE

	err = winterm.SetConsoleMode(fd, mode)
	if err != nil {
		return nil, err
	}
	return state, nil
}

func restoreAtInterrupt(fd uintptr, state *State) {
	sigchan := make(chan os.Signal, 1)
	signal.Notify(sigchan, os.Interrupt)

	go func() {
		_ = <-sigchan
		RestoreTerminal(fd, state)
		os.Exit(0)
	}()
}
<|endoftext|>