<commit_before>/*
Copyright 2019 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 server

import (
	"context"
	"crypto/tls"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"time"

	logf "github.com/jetstack/cert-manager/pkg/logs"

	"github.com/go-logr/logr"
	admissionv1 "k8s.io/api/admission/v1"
	admissionv1beta1 "k8s.io/api/admission/v1beta1"
	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
	apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/runtime/serializer/json"
	ciphers "k8s.io/component-base/cli/flag"
	crlog "sigs.k8s.io/controller-runtime/pkg/log"

	"github.com/jetstack/cert-manager/pkg/util/profiling"
	"github.com/jetstack/cert-manager/pkg/webhook/handlers"
	servertls "github.com/jetstack/cert-manager/pkg/webhook/server/tls"
)

var (
	// defaultScheme is used to encode and decode the AdmissionReview and
	// ConversionReview resources submitted to the webhook server.
	// It is not used for performing validation, mutation or conversion.
	defaultScheme = runtime.NewScheme()
)

func init() {
	admissionv1beta1.AddToScheme(defaultScheme)
	admissionv1.AddToScheme(defaultScheme)
	apiextensionsv1beta1.AddToScheme(defaultScheme)
	apiextensionsv1.AddToScheme(defaultScheme)

	// we need to add the options to empty v1
	// TODO fix the server code to avoid this
	metav1.AddToGroupVersion(defaultScheme, schema.GroupVersion{Version: "v1"})

	// TODO: keep the generic API server from wanting this
	unversioned := schema.GroupVersion{Group: "", Version: "v1"}
	defaultScheme.AddUnversionedTypes(unversioned,
		&metav1.Status{},
		&metav1.APIVersions{},
		&metav1.APIGroupList{},
		&metav1.APIGroup{},
		&metav1.APIResourceList{},
	)
}

type Server struct {
	// ListenAddr is the address the HTTP server should listen on
	// This must be specified.
	ListenAddr string

	// HealthzAddr is the address the healthz HTTP server should listen on
	// If not specified, the healthz endpoint will not be exposed.
	HealthzAddr string

	// EnablePprof controls whether net/http/pprof handlers are registered with
	// the HTTP listener.
	EnablePprof bool

	// Scheme is used to decode/encode request/response payloads.
	// If not specified, a default scheme that registers the AdmissionReview
	// and ConversionReview resource types will be used.
	// It is not used for performing validation, mutation or conversion.
	Scheme *runtime.Scheme

	// If specified, the server will listen with TLS using certificates
	// provided by this CertificateSource.
	CertificateSource servertls.CertificateSource

	ValidationWebhook handlers.ValidatingAdmissionHook
	MutationWebhook   handlers.MutatingAdmissionHook
	ConversionWebhook handlers.ConversionHook

	// Log is an optional logger to write informational and error messages to.
	// If not specified, no messages will be logged.
	Log logr.Logger

	// CipherSuites is the list of allowed cipher suites for the server.
	// Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants).
	CipherSuites []string

	// MinTLSVersion is the minimum TLS version supported.
	// Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants).
	MinTLSVersion string

	listener net.Listener
}

func (s *Server) Run(stopCh <-chan struct{}) error {
	if s.Log == nil {
		s.Log = crlog.NullLogger{}
	}

	internalStopCh := make(chan struct{})
	// only close the internalStopCh if it hasn't already been closed
	shutdown := false
	defer func() {
		if !shutdown {
			close(internalStopCh)
		}
	}()

	var healthzChan <-chan error
	var certSourceChan <-chan error

	// if a HealthzAddr is provided, start the healthz listener
	if s.HealthzAddr != "" {
		l, err := net.Listen("tcp", s.HealthzAddr)
		if err != nil {
			return err
		}

		mux := http.NewServeMux()
		mux.HandleFunc("/healthz", s.handleHealthz)
		mux.HandleFunc("/livez", s.handleLivez)
		s.Log.V(logf.InfoLevel).Info("listening for insecure healthz connections", "address", s.HealthzAddr)
		healthzChan = s.startServer(l, internalStopCh, mux)
	}

	// create a listener for actual webhook requests
	l, err := net.Listen("tcp", s.ListenAddr)
	if err != nil {
		return err
	}
	s.listener = l

	// wrap the listener with TLS if a CertificateSource is provided
	if s.CertificateSource != nil {
		s.Log.V(logf.InfoLevel).Info("listening for secure connections", "address", s.ListenAddr)
		certSourceChan = s.startCertificateSource(internalStopCh)
		cipherSuites, err := ciphers.TLSCipherSuites(s.CipherSuites)
		if err != nil {
			return err
		}
		minVersion, err := ciphers.TLSVersion(s.MinTLSVersion)
		if err != nil {
			return err
		}
		l = tls.NewListener(l, &tls.Config{
			GetCertificate:           s.CertificateSource.GetCertificate,
			CipherSuites:             cipherSuites,
			MinVersion:               minVersion,
			PreferServerCipherSuites: true,
		})
	} else {
		s.Log.V(logf.InfoLevel).Info("listening for insecure connections", "address", s.ListenAddr)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("/validate", s.handle(s.validate))
	mux.HandleFunc("/mutate", s.handle(s.mutate))
	mux.HandleFunc("/convert", s.handle(s.convert))
	if s.EnablePprof {
		profiling.Install(mux)
		s.Log.V(logf.InfoLevel).Info("registered pprof handlers")
	}
	listenerChan := s.startServer(l, internalStopCh, mux)

	if certSourceChan == nil {
		certSourceChan = blockingChan(internalStopCh)
	}
	if healthzChan == nil {
		healthzChan = blockingChan(internalStopCh)
	}

	select {
	case err = <-healthzChan:
	case err = <-certSourceChan:
	case err = <-listenerChan:
	case <-stopCh:
	}

	close(internalStopCh)
	shutdown = true

	s.Log.V(logf.DebugLevel).Info("waiting for server to shutdown")
	waitForAll(healthzChan, certSourceChan, listenerChan)
	s.Log.V(logf.InfoLevel).Info("server shutdown successfully")

	return err
}

// Port returns the port number that the webhook listener is listening on
func (s *Server) Port() (int, error) {
	if s.listener == nil {
		return 0, errors.New("Run() must be called before Port()")
	}
	tcpAddr, ok := s.listener.Addr().(*net.TCPAddr)
	if !ok {
		return 0, errors.New("unexpected listen address type (expected tcp)")
	}
	return tcpAddr.Port, nil
}

func (s *Server) startServer(l net.Listener, stopCh <-chan struct{}, handle http.Handler) <-chan error {
	ch := make(chan error)
	go func() {
		defer close(ch)

		srv := &http.Server{
			Handler: handle,
		}
		select {
		case err := <-channelWrapper(func() error { return srv.Serve(l) }):
			ch <- err
		case <-stopCh:
			// allow a fixed 5s for graceful shutdown
			ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
			defer cancel()
			if err := srv.Shutdown(ctx); err != nil {
				s.Log.Error(err, "failed to gracefully shutdown http server")
				ch <- err
			}
			s.Log.V(logf.DebugLevel).Info("shutdown HTTP server gracefully")
		}
	}()
	return ch
}

func (s *Server) startCertificateSource(stopCh <-chan struct{}) <-chan error {
	fn := func() error {
		return s.CertificateSource.Run(stopCh)
	}
	return channelWrapper(fn)
}

func waitForAll(chs ...<-chan error) error {
	for _, ch := range chs {
		if err := <-ch; err != nil {
			return fmt.Errorf("error waiting for goroutine to exit: %w", err)
		}
	}
	return nil
}

func channelWrapper(fn func() error) <-chan error {
	ch := make(chan error)
	go func() {
		defer close(ch)
		ch <- fn()
	}()
	return ch
}

// blockingChan returns a 'no-op' error channel.
// When stopCh is closed, the error channel will also be closed.
func blockingChan(stopCh <-chan struct{}) <-chan error {
	ch := make(chan error)
	go func() {
		defer close(ch)
		<-stopCh
	}()
	return ch
}

func (s *Server) scheme() *runtime.Scheme {
	if s.Scheme == nil {
		return defaultScheme
	}
	return s.Scheme
}

func (s *Server) validate(obj runtime.Object) (runtime.Object, error) {
	outputVersion := admissionv1.SchemeGroupVersion
	review, isV1 := obj.(*admissionv1.AdmissionReview)
	if !isV1 {
		outputVersion = admissionv1beta1.SchemeGroupVersion
		reviewv1beta1 := obj.(*admissionv1beta1.AdmissionReview)
		convertedReview, err := defaultScheme.ConvertToVersion(reviewv1beta1, admissionv1.SchemeGroupVersion)
		if err != nil {
			return nil, err
		}
		review = convertedReview.(*admissionv1.AdmissionReview)
	}
	resp := s.ValidationWebhook.Validate(review.Request)
	review.Response = resp

	versionedOutput, err := defaultScheme.ConvertToVersion(review, outputVersion)
	return versionedOutput, err
}

func (s *Server) mutate(obj runtime.Object) (runtime.Object, error) {
	outputVersion := admissionv1.SchemeGroupVersion
	review, isV1 := obj.(*admissionv1.AdmissionReview)
	if !isV1 {
		outputVersion = admissionv1beta1.SchemeGroupVersion
		reviewv1beta1 := obj.(*admissionv1beta1.AdmissionReview)
		convertedReview, err := defaultScheme.ConvertToVersion(reviewv1beta1, admissionv1.SchemeGroupVersion)
		if err != nil {
			return nil, err
		}
		review = convertedReview.(*admissionv1.AdmissionReview)
	}
	resp := s.MutationWebhook.Mutate(review.Request)
	review.Response = resp

	versionedOutput, err := defaultScheme.ConvertToVersion(review, outputVersion)
	return versionedOutput, err
}

func (s *Server) convert(obj runtime.Object) (runtime.Object, error) {
	outputVersion := apiextensionsv1.SchemeGroupVersion
	review, isV1 := obj.(*apiextensionsv1.ConversionReview)
	if !isV1 {
		outputVersion = apiextensionsv1beta1.SchemeGroupVersion
		reviewv1beta1 := obj.(*admissionv1beta1.AdmissionReview)
		convertedReview, err := defaultScheme.ConvertToVersion(reviewv1beta1, admissionv1.SchemeGroupVersion)
		if err != nil {
			return nil, err
		}
		review = convertedReview.(*apiextensionsv1.ConversionReview)
	}

	resp := s.ConversionWebhook.Convert(review.Request)
	review.Response = resp

	versionedOutput, err := defaultScheme.ConvertToVersion(review, outputVersion)
	return versionedOutput, err
}

func (s *Server) handle(inner func(runtime.Object) (runtime.Object, error)) func(w http.ResponseWriter, req *http.Request) {
	return func(w http.ResponseWriter, req *http.Request) {
		defer req.Body.Close()

		data, err := ioutil.ReadAll(req.Body)
		if err != nil {
			s.Log.Error(err, "failed to read request body")
			w.WriteHeader(http.StatusBadRequest)
			return
		}

		codec := json.NewSerializerWithOptions(json.DefaultMetaFactory, s.scheme(), s.scheme(), json.SerializerOptions{
			Pretty: true,
		})
		codec.Decode(data, nil, nil)
		obj, _, err := codec.Decode(data, nil, nil)
		if err != nil {
			s.Log.Error(err, "failed to decode request body")
			w.WriteHeader(http.StatusBadRequest)
			return
		}

		result, err := inner(obj)
		if err != nil {
			s.Log.Error(err, "failed to process webhook request")
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		if err := codec.Encode(result, w); err != nil {
			s.Log.Error(err, "failed to encode response body")
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
	}
}

func (s *Server) handleHealthz(w http.ResponseWriter, req *http.Request) {
	defer req.Body.Close()

	if s.CertificateSource != nil && !s.CertificateSource.Healthy() {
		s.Log.V(logf.WarnLevel).Info("Health check failed as CertificateSource is unhealthy")
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
}

func (s *Server) handleLivez(w http.ResponseWriter, req *http.Request) {
	defer req.Body.Close()

	w.WriteHeader(http.StatusOK)
}
<commit_msg>Add better error<commit_after>/*
Copyright 2019 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 server

import (
	"context"
	"crypto/tls"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"time"

	logf "github.com/jetstack/cert-manager/pkg/logs"

	"github.com/go-logr/logr"
	admissionv1 "k8s.io/api/admission/v1"
	admissionv1beta1 "k8s.io/api/admission/v1beta1"
	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
	apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/runtime/serializer/json"
	ciphers "k8s.io/component-base/cli/flag"
	crlog "sigs.k8s.io/controller-runtime/pkg/log"

	"github.com/jetstack/cert-manager/pkg/util/profiling"
	"github.com/jetstack/cert-manager/pkg/webhook/handlers"
	servertls "github.com/jetstack/cert-manager/pkg/webhook/server/tls"
)

var (
	// defaultScheme is used to encode and decode the AdmissionReview and
	// ConversionReview resources submitted to the webhook server.
	// It is not used for performing validation, mutation or conversion.
	defaultScheme = runtime.NewScheme()
)

func init() {
	admissionv1beta1.AddToScheme(defaultScheme)
	admissionv1.AddToScheme(defaultScheme)
	apiextensionsv1beta1.AddToScheme(defaultScheme)
	apiextensionsv1.AddToScheme(defaultScheme)

	// we need to add the options to empty v1
	// TODO fix the server code to avoid this
	metav1.AddToGroupVersion(defaultScheme, schema.GroupVersion{Version: "v1"})

	// TODO: keep the generic API server from wanting this
	unversioned := schema.GroupVersion{Group: "", Version: "v1"}
	defaultScheme.AddUnversionedTypes(unversioned,
		&metav1.Status{},
		&metav1.APIVersions{},
		&metav1.APIGroupList{},
		&metav1.APIGroup{},
		&metav1.APIResourceList{},
	)
}

type Server struct {
	// ListenAddr is the address the HTTP server should listen on
	// This must be specified.
	ListenAddr string

	// HealthzAddr is the address the healthz HTTP server should listen on
	// If not specified, the healthz endpoint will not be exposed.
	HealthzAddr string

	// EnablePprof controls whether net/http/pprof handlers are registered with
	// the HTTP listener.
	EnablePprof bool

	// Scheme is used to decode/encode request/response payloads.
	// If not specified, a default scheme that registers the AdmissionReview
	// and ConversionReview resource types will be used.
	// It is not used for performing validation, mutation or conversion.
	Scheme *runtime.Scheme

	// If specified, the server will listen with TLS using certificates
	// provided by this CertificateSource.
	CertificateSource servertls.CertificateSource

	ValidationWebhook handlers.ValidatingAdmissionHook
	MutationWebhook   handlers.MutatingAdmissionHook
	ConversionWebhook handlers.ConversionHook

	// Log is an optional logger to write informational and error messages to.
	// If not specified, no messages will be logged.
	Log logr.Logger

	// CipherSuites is the list of allowed cipher suites for the server.
	// Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants).
	CipherSuites []string

	// MinTLSVersion is the minimum TLS version supported.
	// Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants).
	MinTLSVersion string

	listener net.Listener
}

func (s *Server) Run(stopCh <-chan struct{}) error {
	if s.Log == nil {
		s.Log = crlog.NullLogger{}
	}

	internalStopCh := make(chan struct{})
	// only close the internalStopCh if it hasn't already been closed
	shutdown := false
	defer func() {
		if !shutdown {
			close(internalStopCh)
		}
	}()

	var healthzChan <-chan error
	var certSourceChan <-chan error

	// if a HealthzAddr is provided, start the healthz listener
	if s.HealthzAddr != "" {
		l, err := net.Listen("tcp", s.HealthzAddr)
		if err != nil {
			return err
		}

		mux := http.NewServeMux()
		mux.HandleFunc("/healthz", s.handleHealthz)
		mux.HandleFunc("/livez", s.handleLivez)
		s.Log.V(logf.InfoLevel).Info("listening for insecure healthz connections", "address", s.HealthzAddr)
		healthzChan = s.startServer(l, internalStopCh, mux)
	}

	// create a listener for actual webhook requests
	l, err := net.Listen("tcp", s.ListenAddr)
	if err != nil {
		return err
	}
	s.listener = l

	// wrap the listener with TLS if a CertificateSource is provided
	if s.CertificateSource != nil {
		s.Log.V(logf.InfoLevel).Info("listening for secure connections", "address", s.ListenAddr)
		certSourceChan = s.startCertificateSource(internalStopCh)
		cipherSuites, err := ciphers.TLSCipherSuites(s.CipherSuites)
		if err != nil {
			return err
		}
		minVersion, err := ciphers.TLSVersion(s.MinTLSVersion)
		if err != nil {
			return err
		}
		l = tls.NewListener(l, &tls.Config{
			GetCertificate:           s.CertificateSource.GetCertificate,
			CipherSuites:             cipherSuites,
			MinVersion:               minVersion,
			PreferServerCipherSuites: true,
		})
	} else {
		s.Log.V(logf.InfoLevel).Info("listening for insecure connections", "address", s.ListenAddr)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("/validate", s.handle(s.validate))
	mux.HandleFunc("/mutate", s.handle(s.mutate))
	mux.HandleFunc("/convert", s.handle(s.convert))
	if s.EnablePprof {
		profiling.Install(mux)
		s.Log.V(logf.InfoLevel).Info("registered pprof handlers")
	}
	listenerChan := s.startServer(l, internalStopCh, mux)

	if certSourceChan == nil {
		certSourceChan = blockingChan(internalStopCh)
	}
	if healthzChan == nil {
		healthzChan = blockingChan(internalStopCh)
	}

	select {
	case err = <-healthzChan:
	case err = <-certSourceChan:
	case err = <-listenerChan:
	case <-stopCh:
	}

	close(internalStopCh)
	shutdown = true

	s.Log.V(logf.DebugLevel).Info("waiting for server to shutdown")
	waitForAll(healthzChan, certSourceChan, listenerChan)
	s.Log.V(logf.InfoLevel).Info("server shutdown successfully")

	return err
}

// Port returns the port number that the webhook listener is listening on
func (s *Server) Port() (int, error) {
	if s.listener == nil {
		return 0, errors.New("Run() must be called before Port()")
	}
	tcpAddr, ok := s.listener.Addr().(*net.TCPAddr)
	if !ok {
		return 0, errors.New("unexpected listen address type (expected tcp)")
	}
	return tcpAddr.Port, nil
}

func (s *Server) startServer(l net.Listener, stopCh <-chan struct{}, handle http.Handler) <-chan error {
	ch := make(chan error)
	go func() {
		defer close(ch)

		srv := &http.Server{
			Handler: handle,
		}
		select {
		case err := <-channelWrapper(func() error { return srv.Serve(l) }):
			ch <- err
		case <-stopCh:
			// allow a fixed 5s for graceful shutdown
			ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
			defer cancel()
			if err := srv.Shutdown(ctx); err != nil {
				s.Log.Error(err, "failed to gracefully shutdown http server")
				ch <- err
			}
			s.Log.V(logf.DebugLevel).Info("shutdown HTTP server gracefully")
		}
	}()
	return ch
}

func (s *Server) startCertificateSource(stopCh <-chan struct{}) <-chan error {
	fn := func() error {
		return s.CertificateSource.Run(stopCh)
	}
	return channelWrapper(fn)
}

func waitForAll(chs ...<-chan error) error {
	for _, ch := range chs {
		if err := <-ch; err != nil {
			return fmt.Errorf("error waiting for goroutine to exit: %w", err)
		}
	}
	return nil
}

func channelWrapper(fn func() error) <-chan error {
	ch := make(chan error)
	go func() {
		defer close(ch)
		ch <- fn()
	}()
	return ch
}

// blockingChan returns a 'no-op' error channel.
// When stopCh is closed, the error channel will also be closed.
func blockingChan(stopCh <-chan struct{}) <-chan error {
	ch := make(chan error)
	go func() {
		defer close(ch)
		<-stopCh
	}()
	return ch
}

func (s *Server) scheme() *runtime.Scheme {
	if s.Scheme == nil {
		return defaultScheme
	}
	return s.Scheme
}

func (s *Server) validate(obj runtime.Object) (runtime.Object, error) {
	outputVersion := admissionv1.SchemeGroupVersion
	review, isV1 := obj.(*admissionv1.AdmissionReview)
	if !isV1 {
		outputVersion = admissionv1beta1.SchemeGroupVersion
		reviewv1beta1 := obj.(*admissionv1beta1.AdmissionReview)
		convertedReview, err := defaultScheme.ConvertToVersion(reviewv1beta1, admissionv1.SchemeGroupVersion)
		if err != nil {
			return nil, err
		}
		review = convertedReview.(*admissionv1.AdmissionReview)
	}
	resp := s.ValidationWebhook.Validate(review.Request)
	review.Response = resp

	versionedOutput, err := defaultScheme.ConvertToVersion(review, outputVersion)
	return versionedOutput, err
}

func (s *Server) mutate(obj runtime.Object) (runtime.Object, error) {
	outputVersion := admissionv1.SchemeGroupVersion
	review, isV1 := obj.(*admissionv1.AdmissionReview)
	if !isV1 {
		outputVersion = admissionv1beta1.SchemeGroupVersion
		reviewv1beta1 := obj.(*admissionv1beta1.AdmissionReview)
		convertedReview, err := defaultScheme.ConvertToVersion(reviewv1beta1, admissionv1.SchemeGroupVersion)
		if err != nil {
			return nil, err
		}
		review = convertedReview.(*admissionv1.AdmissionReview)
	}
	resp := s.MutationWebhook.Mutate(review.Request)
	review.Response = resp

	versionedOutput, err := defaultScheme.ConvertToVersion(review, outputVersion)
	return versionedOutput, err
}

func (s *Server) convert(obj runtime.Object) (runtime.Object, error) {
	outputVersion := apiextensionsv1.SchemeGroupVersion
	review, isV1 := obj.(*apiextensionsv1.ConversionReview)
	if !isV1 {
		outputVersion = apiextensionsv1beta1.SchemeGroupVersion
		reviewv1beta1, isV1beta1 := obj.(*admissionv1beta1.AdmissionReview)
		if !isV1beta1 {
			return nil, errors.New("request is not of type apiextensions v1 or v1beta1")
		}
		convertedReview, err := defaultScheme.ConvertToVersion(reviewv1beta1, admissionv1.SchemeGroupVersion)
		if err != nil {
			return nil, err
		}
		review = convertedReview.(*apiextensionsv1.ConversionReview)
	}

	resp := s.ConversionWebhook.Convert(review.Request)
	review.Response = resp

	versionedOutput, err := defaultScheme.ConvertToVersion(review, outputVersion)
	return versionedOutput, err
}

func (s *Server) handle(inner func(runtime.Object) (runtime.Object, error)) func(w http.ResponseWriter, req *http.Request) {
	return func(w http.ResponseWriter, req *http.Request) {
		defer req.Body.Close()

		data, err := ioutil.ReadAll(req.Body)
		if err != nil {
			s.Log.Error(err, "failed to read request body")
			w.WriteHeader(http.StatusBadRequest)
			return
		}

		codec := json.NewSerializerWithOptions(json.DefaultMetaFactory, s.scheme(), s.scheme(), json.SerializerOptions{
			Pretty: true,
		})
		codec.Decode(data, nil, nil)
		obj, _, err := codec.Decode(data, nil, nil)
		if err != nil {
			s.Log.Error(err, "failed to decode request body")
			w.WriteHeader(http.StatusBadRequest)
			return
		}

		result, err := inner(obj)
		if err != nil {
			s.Log.Error(err, "failed to process webhook request")
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		if err := codec.Encode(result, w); err != nil {
			s.Log.Error(err, "failed to encode response body")
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
	}
}

func (s *Server) handleHealthz(w http.ResponseWriter, req *http.Request) {
	defer req.Body.Close()

	if s.CertificateSource != nil && !s.CertificateSource.Healthy() {
		s.Log.V(logf.WarnLevel).Info("Health check failed as CertificateSource is unhealthy")
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
}

func (s *Server) handleLivez(w http.ResponseWriter, req *http.Request) {
	defer req.Body.Close()

	w.WriteHeader(http.StatusOK)
}
<|endoftext|>


<commit_before>package geodata

import (
	"fmt"

	"github.com/golang/geo/s2"
	"github.com/golang/protobuf/ptypes/struct"
	spb "github.com/golang/protobuf/ptypes/struct"
	"github.com/pkg/errors"
	"github.com/twpayne/go-geom"
	"github.com/twpayne/go-geom/encoding/geojson"
)

// GeomToGeoData update gd with geo data gathered from g
func GeomToGeoData(g geom.T, gd *GeoData) error {
	geo := &Geometry{}

	switch g := g.(type) {
	case *geom.Point:
		geo.Coordinates = g.Coords()
		geo.Type = Geometry_POINT
		//case *geom.MultiPolygon:
		//	geo.Type = geodata.Geometry_MULTIPOLYGON

	case *geom.Polygon:
		// only supports outer ring
		geo.Type = Geometry_POLYGON
		geo.Coordinates = g.FlatCoords()

	case *geom.LineString:
		geo.Type = Geometry_LINESTRING
		geo.Coordinates = g.FlatCoords()

	default:
		return errors.Errorf("unsupported geo type %T", g)
	}

	gd.Geometry = geo
	return nil
}

// GeoDataToGeom converts GeoData to a geom.T representation
func GeoDataToGeom(gd *GeoData) (geom.T, error) {
	switch gd.Geometry.Type {
	case Geometry_POINT:
		return geom.NewPointFlat(geom.XY, gd.Geometry.Coordinates), nil
	case Geometry_POLYGON:
		return geom.NewPolygonFlat(geom.XY, gd.Geometry.Coordinates, []int{len(gd.Geometry.Coordinates)}), nil
	case Geometry_LINESTRING:
		return geom.NewLineStringFlat(geom.XY, gd.Geometry.Coordinates), nil
	default:
		return nil, errors.Errorf("unsupported geodata type")
	}
}

// GeoJSONFeatureToGeoData fill gd with the GeoJSON data f
func GeoJSONFeatureToGeoData(f *geojson.Feature, gd *GeoData) error {
	err := PropertiesToGeoData(f, gd)
	if err != nil {
		return errors.Wrap(err, "while converting feature properties to GeoData")
	}

	err = GeomToGeoData(f.Geometry, gd)
	if err != nil {
		return errors.Wrap(err, "while converting feature to GeoData")
	}

	return nil
}

// PropertiesToGeoData update gd.Properties with the properties found in f
func PropertiesToGeoData(f *geojson.Feature, gd *GeoData) error {
	if gd.Properties == nil {
		gd.Properties = make(map[string]*structpb.Value)
	}
	for k, vi := range f.Properties {
		switch tv := vi.(type) {
		case bool:
			gd.Properties[k] = &structpb.Value{Kind: &structpb.Value_BoolValue{BoolValue: tv}}
		case int:
			gd.Properties[k] = &structpb.Value{Kind: &structpb.Value_NumberValue{NumberValue: float64(tv)}}
		case string:
			gd.Properties[k] = &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: tv}}
		case float64:
			gd.Properties[k] = &structpb.Value{Kind: &structpb.Value_NumberValue{NumberValue: tv}}
		case nil:
			// pass
		default:
			return fmt.Errorf("GeoJSON property %s unsupported type %T", k, tv)
		}
	}
	return nil
}

// GeoDataToRect generate a RectBound for GeoData gd
// only works with Polygons & LineString
func GeoDataToRect(gd *GeoData) (s2.Rect, error) {
	if gd.Geometry == nil {
		return s2.Rect{}, errors.New("invalid geometry")
	}
	switch gd.Geometry.Type {
	case Geometry_POINT:
		return s2.Rect{}, errors.New("point can't be rect bounded")

	case Geometry_POLYGON:
		l := LoopFromCoordinates(gd.Geometry.Coordinates)
		if l.IsEmpty() || l.IsFull() || l.ContainsOrigin() {
			return s2.Rect{}, errors.New("invalid polygon")
		}
		return l.RectBound(), nil

	case Geometry_MULTIPOLYGON:
		return s2.Rect{}, errors.New("multipolygon not supported")

	case Geometry_LINESTRING:
		if len(gd.Geometry.Coordinates)%2 != 0 {
			return s2.Rect{}, errors.New("invalid coordinates count for line")
		}

		pl := make(s2.Polyline, len(gd.Geometry.Coordinates)/2)
		for i := 0; i < len(gd.Geometry.Coordinates); i += 2 {
			ll := s2.LatLngFromDegrees(gd.Geometry.Coordinates[i+1], gd.Geometry.Coordinates[i])
			pl[i/2] = s2.PointFromLatLng(ll)
		}

		return pl.RectBound(), nil

	default:
		return s2.Rect{}, errors.New("unsupported data type")
	}

	return s2.Rect{}, nil
}

// GeoDataToCellUnion generate an s2 cover for GeoData gd
func GeoDataToCellUnion(gd *GeoData, coverer *s2.RegionCoverer) (s2.CellUnion, error) {
	if gd.Geometry == nil {
		return nil, errors.New("invalid geometry")
	}
	var cu s2.CellUnion
	switch gd.Geometry.Type {
	case Geometry_POINT:
		c := s2.CellIDFromLatLng(s2.LatLngFromDegrees(gd.Geometry.Coordinates[1], gd.Geometry.Coordinates[0]))
		cu = append(cu, c.Parent(coverer.MinLevel))

	case Geometry_POLYGON:
		cup, err := coverPolygon(gd.Geometry.Coordinates, coverer)
		if err != nil {
			return nil, errors.Wrap(err, "can't cover polygon")
		}
		cu = append(cu, cup...)

	case Geometry_MULTIPOLYGON:
		for _, g := range gd.Geometry.Geometries {
			cup, err := coverPolygon(g.Coordinates, coverer)
			if err != nil {
				return nil, errors.Wrap(err, "can't cover multipolygon")
			}

			cu = append(cu, cup...)
		}

	case Geometry_LINESTRING:
		if len(gd.Geometry.Coordinates)%2 != 0 {
			return nil, errors.New("invalid coordinates count for line")
		}

		pl := make(s2.Polyline, len(gd.Geometry.Coordinates)/2)
		for i := 0; i < len(gd.Geometry.Coordinates); i += 2 {
			ll := s2.LatLngFromDegrees(gd.Geometry.Coordinates[i+1], gd.Geometry.Coordinates[i])
			pl[i/2] = s2.PointFromLatLng(ll)
		}

		cupl := coverer.Covering(&pl)
		cu = append(cu, cupl...)

	default:
		return nil, errors.New("unsupported data type")
	}

	return cu, nil
}

// returns an s2 cover from a list of lng, lat forming a closed polygon
func coverPolygon(c []float64, coverer *s2.RegionCoverer) (s2.CellUnion, error) {
	if len(c) < 6 {
		return nil, errors.New("invalid polygons not enough coordinates for a closed polygon")
	}
	if len(c)%2 != 0 {
		return nil, errors.New("invalid polygons odd coordinates number")
	}
	l := LoopFromCoordinates(c)
	if l.IsEmpty() || l.IsFull() || l.ContainsOrigin() {
		return nil, errors.New("invalid polygons")
	}

	return coverer.Covering(l), nil
}

// ToGeoJSONFeatureCollection converts a list of GeoData to a GeoJSON Feature Collection
func ToGeoJSONFeatureCollection(geos []*GeoData) ([]byte, error) {
	fc := geojson.FeatureCollection{}
	for _, g := range geos {
		f := &geojson.Feature{}
		switch g.Geometry.Type {
		case Geometry_POINT:
			ng := geom.NewPointFlat(geom.XY, g.Geometry.Coordinates)
			f.Geometry = ng
		case Geometry_POLYGON:
			ng := geom.NewPolygonFlat(geom.XY, g.Geometry.Coordinates, []int{len(g.Geometry.Coordinates)})
			f.Geometry = ng
		case Geometry_MULTIPOLYGON:
			mp := geom.NewMultiPolygon(geom.XY)
			for _, poly := range g.Geometry.Geometries {
				ng := geom.NewPolygonFlat(geom.XY, poly.Coordinates, []int{len(poly.Coordinates)})
				mp.Push(ng)
			}
			f.Geometry = mp
		case Geometry_LINESTRING:
			ls := geom.NewLineStringFlat(geom.XY, g.Geometry.Coordinates)
			f.Geometry = ls
		}
		f.Properties = PropertiesToJSONMap(g.Properties)
		fc.Features = append(fc.Features, f)
	}

	return fc.MarshalJSON()
}

// PointsToGeoJSONPolyLines converts a list of GeoData containing points to a polylines GeoJSON
func PointsToGeoJSONPolyLines(geos []*GeoData) ([]byte, error) {
	f := geojson.Feature{}
	var flatCoords []float64

	if len(geos) == 0 {
		return f.MarshalJSON()
	}

	for _, g := range geos {
		switch g.Geometry.Type {
		case Geometry_POINT:
			flatCoords = append(flatCoords, g.Geometry.Coordinates...)
		default:
			return nil, errors.Errorf("unsupported geometry")
		}

	}
	f.Properties = PropertiesToJSONMap(geos[0].Properties)
	g := geom.NewLineStringFlat(geom.XY, flatCoords)
	f.Geometry = g

	return f.MarshalJSON()
}

// PropertiesToJSONMap converts a protobuf map to it's JSON serializable map equivalent
func PropertiesToJSONMap(src map[string]*spb.Value) map[string]interface{} {
	res := make(map[string]interface{})

	for k, v := range src {
		switch x := v.Kind.(type) {
		case *spb.Value_NumberValue:
			res[k] = x.NumberValue
		case *spb.Value_StringValue:
			res[k] = x.StringValue
		case *spb.Value_BoolValue:
			res[k] = x.BoolValue
		}
	}
	return res
}
<commit_msg>Change gd.Properties only when required<commit_after>package geodata

import (
	"fmt"

	"github.com/golang/geo/s2"
	"github.com/golang/protobuf/ptypes/struct"
	spb "github.com/golang/protobuf/ptypes/struct"
	"github.com/pkg/errors"
	"github.com/twpayne/go-geom"
	"github.com/twpayne/go-geom/encoding/geojson"
)

// GeomToGeoData update gd with geo data gathered from g
func GeomToGeoData(g geom.T, gd *GeoData) error {
	geo := &Geometry{}

	switch g := g.(type) {
	case *geom.Point:
		geo.Coordinates = g.Coords()
		geo.Type = Geometry_POINT
		//case *geom.MultiPolygon:
		//	geo.Type = geodata.Geometry_MULTIPOLYGON

	case *geom.Polygon:
		// only supports outer ring
		geo.Type = Geometry_POLYGON
		geo.Coordinates = g.FlatCoords()

	case *geom.LineString:
		geo.Type = Geometry_LINESTRING
		geo.Coordinates = g.FlatCoords()

	default:
		return errors.Errorf("unsupported geo type %T", g)
	}

	gd.Geometry = geo
	return nil
}

// GeoDataToGeom converts GeoData to a geom.T representation
func GeoDataToGeom(gd *GeoData) (geom.T, error) {
	switch gd.Geometry.Type {
	case Geometry_POINT:
		return geom.NewPointFlat(geom.XY, gd.Geometry.Coordinates), nil
	case Geometry_POLYGON:
		return geom.NewPolygonFlat(geom.XY, gd.Geometry.Coordinates, []int{len(gd.Geometry.Coordinates)}), nil
	case Geometry_LINESTRING:
		return geom.NewLineStringFlat(geom.XY, gd.Geometry.Coordinates), nil
	default:
		return nil, errors.Errorf("unsupported geodata type")
	}
}

// GeoJSONFeatureToGeoData fill gd with the GeoJSON data f
func GeoJSONFeatureToGeoData(f *geojson.Feature, gd *GeoData) error {
	err := PropertiesToGeoData(f, gd)
	if err != nil {
		return errors.Wrap(err, "while converting feature properties to GeoData")
	}

	err = GeomToGeoData(f.Geometry, gd)
	if err != nil {
		return errors.Wrap(err, "while converting feature to GeoData")
	}

	return nil
}

// PropertiesToGeoData update gd.Properties with the properties found in f
func PropertiesToGeoData(f *geojson.Feature, gd *GeoData) error {
	m := make(map[string]*structpb.Value)
	for k, vi := range f.Properties {
		switch tv := vi.(type) {
		case bool:
			m[k] = &structpb.Value{Kind: &structpb.Value_BoolValue{BoolValue: tv}}
		case int:
			m[k] = &structpb.Value{Kind: &structpb.Value_NumberValue{NumberValue: float64(tv)}}
		case string:
			m[k] = &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: tv}}
		case float64:
			m[k] = &structpb.Value{Kind: &structpb.Value_NumberValue{NumberValue: tv}}
		case nil:
			// pass
		default:
			return fmt.Errorf("GeoJSON property %s unsupported type %T", k, tv)
		}
	}
	if len(m) > 0 {
		gd.Properties = make(map[string]*structpb.Value)
	}
	for k, v := range m {
		gd.Properties[k] = v
	}
	return nil
}

// GeoDataToRect generate a RectBound for GeoData gd
// only works with Polygons & LineString
func GeoDataToRect(gd *GeoData) (s2.Rect, error) {
	if gd.Geometry == nil {
		return s2.Rect{}, errors.New("invalid geometry")
	}
	switch gd.Geometry.Type {
	case Geometry_POINT:
		return s2.Rect{}, errors.New("point can't be rect bounded")

	case Geometry_POLYGON:
		l := LoopFromCoordinates(gd.Geometry.Coordinates)
		if l.IsEmpty() || l.IsFull() || l.ContainsOrigin() {
			return s2.Rect{}, errors.New("invalid polygon")
		}
		return l.RectBound(), nil

	case Geometry_MULTIPOLYGON:
		return s2.Rect{}, errors.New("multipolygon not supported")

	case Geometry_LINESTRING:
		if len(gd.Geometry.Coordinates)%2 != 0 {
			return s2.Rect{}, errors.New("invalid coordinates count for line")
		}

		pl := make(s2.Polyline, len(gd.Geometry.Coordinates)/2)
		for i := 0; i < len(gd.Geometry.Coordinates); i += 2 {
			ll := s2.LatLngFromDegrees(gd.Geometry.Coordinates[i+1], gd.Geometry.Coordinates[i])
			pl[i/2] = s2.PointFromLatLng(ll)
		}

		return pl.RectBound(), nil

	default:
		return s2.Rect{}, errors.New("unsupported data type")
	}

	return s2.Rect{}, nil
}

// GeoDataToCellUnion generate an s2 cover for GeoData gd
func GeoDataToCellUnion(gd *GeoData, coverer *s2.RegionCoverer) (s2.CellUnion, error) {
	if gd.Geometry == nil {
		return nil, errors.New("invalid geometry")
	}
	var cu s2.CellUnion
	switch gd.Geometry.Type {
	case Geometry_POINT:
		c := s2.CellIDFromLatLng(s2.LatLngFromDegrees(gd.Geometry.Coordinates[1], gd.Geometry.Coordinates[0]))
		cu = append(cu, c.Parent(coverer.MinLevel))

	case Geometry_POLYGON:
		cup, err := coverPolygon(gd.Geometry.Coordinates, coverer)
		if err != nil {
			return nil, errors.Wrap(err, "can't cover polygon")
		}
		cu = append(cu, cup...)

	case Geometry_MULTIPOLYGON:
		for _, g := range gd.Geometry.Geometries {
			cup, err := coverPolygon(g.Coordinates, coverer)
			if err != nil {
				return nil, errors.Wrap(err, "can't cover multipolygon")
			}

			cu = append(cu, cup...)
		}

	case Geometry_LINESTRING:
		if len(gd.Geometry.Coordinates)%2 != 0 {
			return nil, errors.New("invalid coordinates count for line")
		}

		pl := make(s2.Polyline, len(gd.Geometry.Coordinates)/2)
		for i := 0; i < len(gd.Geometry.Coordinates); i += 2 {
			ll := s2.LatLngFromDegrees(gd.Geometry.Coordinates[i+1], gd.Geometry.Coordinates[i])
			pl[i/2] = s2.PointFromLatLng(ll)
		}

		cupl := coverer.Covering(&pl)
		cu = append(cu, cupl...)

	default:
		return nil, errors.New("unsupported data type")
	}

	return cu, nil
}

// returns an s2 cover from a list of lng, lat forming a closed polygon
func coverPolygon(c []float64, coverer *s2.RegionCoverer) (s2.CellUnion, error) {
	if len(c) < 6 {
		return nil, errors.New("invalid polygons not enough coordinates for a closed polygon")
	}
	if len(c)%2 != 0 {
		return nil, errors.New("invalid polygons odd coordinates number")
	}
	l := LoopFromCoordinates(c)
	if l.IsEmpty() || l.IsFull() || l.ContainsOrigin() {
		return nil, errors.New("invalid polygons")
	}

	return coverer.Covering(l), nil
}

// ToGeoJSONFeatureCollection converts a list of GeoData to a GeoJSON Feature Collection
func ToGeoJSONFeatureCollection(geos []*GeoData) ([]byte, error) {
	fc := geojson.FeatureCollection{}
	for _, g := range geos {
		f := &geojson.Feature{}
		switch g.Geometry.Type {
		case Geometry_POINT:
			ng := geom.NewPointFlat(geom.XY, g.Geometry.Coordinates)
			f.Geometry = ng
		case Geometry_POLYGON:
			ng := geom.NewPolygonFlat(geom.XY, g.Geometry.Coordinates, []int{len(g.Geometry.Coordinates)})
			f.Geometry = ng
		case Geometry_MULTIPOLYGON:
			mp := geom.NewMultiPolygon(geom.XY)
			for _, poly := range g.Geometry.Geometries {
				ng := geom.NewPolygonFlat(geom.XY, poly.Coordinates, []int{len(poly.Coordinates)})
				mp.Push(ng)
			}
			f.Geometry = mp
		case Geometry_LINESTRING:
			ls := geom.NewLineStringFlat(geom.XY, g.Geometry.Coordinates)
			f.Geometry = ls
		}
		f.Properties = PropertiesToJSONMap(g.Properties)
		fc.Features = append(fc.Features, f)
	}

	return fc.MarshalJSON()
}

// PointsToGeoJSONPolyLines converts a list of GeoData containing points to a polylines GeoJSON
func PointsToGeoJSONPolyLines(geos []*GeoData) ([]byte, error) {
	f := geojson.Feature{}
	var flatCoords []float64

	if len(geos) == 0 {
		return f.MarshalJSON()
	}

	for _, g := range geos {
		switch g.Geometry.Type {
		case Geometry_POINT:
			flatCoords = append(flatCoords, g.Geometry.Coordinates...)
		default:
			return nil, errors.Errorf("unsupported geometry")
		}

	}
	f.Properties = PropertiesToJSONMap(geos[0].Properties)
	g := geom.NewLineStringFlat(geom.XY, flatCoords)
	f.Geometry = g

	return f.MarshalJSON()
}

// PropertiesToJSONMap converts a protobuf map to it's JSON serializable map equivalent
func PropertiesToJSONMap(src map[string]*spb.Value) map[string]interface{} {
	res := make(map[string]interface{})

	for k, v := range src {
		switch x := v.Kind.(type) {
		case *spb.Value_NumberValue:
			res[k] = x.NumberValue
		case *spb.Value_StringValue:
			res[k] = x.StringValue
		case *spb.Value_BoolValue:
			res[k] = x.BoolValue
		}
	}
	return res
}
<|endoftext|>


<commit_before>// timeutil provides a set of time utilities including comparisons,
// conversion to "DT8" int32 and "DT14" int64 formats and other
// capabilities.
package timeutil

import (
	"errors"
	"fmt"
	"strings"
	"time"
)

const (
	DT14            = "20060102150405"
	DT6             = "200601"
	DT8             = "20060102"
	RFC3339Min      = "0000-01-01T00:00:00Z"
	RFC3339Zero     = "0001-01-01T00:00:00Z"
	RFC3339YMD      = "2006-01-02"
	ISO8601YM       = "2006-01"
	ISO8601Z2       = "2006-01-02T15:04:05-07"
	ISO8601Z4       = "2006-01-02T15:04:05-0700"
	ISO8601ZCompact = "20060102T150405Z"
)

var FormatMap = map[string]string{
	"RFC3339":    time.RFC3339,
	"RFC3339YMD": RFC3339YMD,
	"ISO8601YM":  ISO8601YM,
}

func GetFormat(formatName string) (string, error) {
	format, ok := FormatMap[strings.TrimSpace(formatName)]
	if !ok {
		return "", errors.New(fmt.Sprintf("Format Not Found: %v", format))
	}
	return format, nil
}
<commit_msg>add timeutil.FormatQuarter()<commit_after>// timeutil provides a set of time utilities including comparisons,
// conversion to "DT8" int32 and "DT14" int64 formats and other
// capabilities.
package timeutil

import (
	"errors"
	"fmt"
	"strings"
	"time"
)

const (
	DT14            = "20060102150405"
	DT6             = "200601"
	DT8             = "20060102"
	RFC3339Min      = "0000-01-01T00:00:00Z"
	RFC3339Zero     = "0001-01-01T00:00:00Z"
	RFC3339YMD      = "2006-01-02"
	ISO8601YM       = "2006-01"
	ISO8601Z2       = "2006-01-02T15:04:05-07"
	ISO8601Z4       = "2006-01-02T15:04:05-0700"
	ISO8601ZCompact = "20060102T150405Z"
)

var FormatMap = map[string]string{
	"RFC3339":    time.RFC3339,
	"RFC3339YMD": RFC3339YMD,
	"ISO8601YM":  ISO8601YM,
}

func GetFormat(formatName string) (string, error) {
	format, ok := FormatMap[strings.TrimSpace(formatName)]
	if !ok {
		return "", errors.New(fmt.Sprintf("Format Not Found: %v", format))
	}
	return format, nil
}

func FormatQuarter(t time.Time) string {
	return fmt.Sprintf("%d Q%d", t.Year(), MonthToQuarter(int(t.Month())))
}
<|endoftext|>


<commit_before>// timeutil provides a set of time utilities including comparisons,
// conversion to "DT8" int32 and "DT14" int64 formats and other
// capabilities.
package timeutil

import (
	"fmt"
	"strings"
	"time"
)

// More predefined layouts for use in Time.Format and time.Parse.
const (
	DT14               = "20060102150405"
	DT8                = "20060102"
	DT6                = "200601"
	RFC3339FullDate    = "2006-01-02"
	ISO8601            = "2006-01-02T15:04:05Z0700"
	ISO8601TZHour      = "2006-01-02T15:04:05Z07"
	ISO8601MilliNoTZ   = "2006-01-02T15:04:05.000"
	ISO8601CompactZ    = "20060102T150405Z0700"
	ISO8601CompactNoTZ = "20060102T150405"
	ISO8601YM          = "2006-01"
	InsightlyApiQuery  = "_1/_2/2006 _3:04:05 PM"
	SQLTimestamp       = "2006-01-02 15:04:05" // MySQL, BigQuery, etc.
	DateMDYSlash       = "01/02/2006"
	DateDMYHM2         = "02:01:06 15:04" // GMT time in format dd:mm:yy hh:mm
)

const (
	RFC3339Min         = "0000-01-01T00:00:00Z"
	RFC3339Max         = "9999-12-31T23:59:59Z"
	RFC3339Zero        = "0001-01-01T00:00:00Z"
	RFC3339ZeroUnix    = "1970-01-01T00:00:00Z"
	RFC3339YMDZeroUnix = int64(-62135596800)
)

// Reformat a time string from one format to another
func FromTo(value, fromLayout, toLayout string) (string, error) {
	t, err := time.Parse(fromLayout, strings.TrimSpace(value))
	if err != nil {
		return "", err
	}
	return t.Format(toLayout), nil
}

// ParseFirst attempts to parse a string with a set of layouts.
func ParseFirst(layouts []string, value string) (time.Time, error) {
	value = strings.TrimSpace(value)
	if len(value) == 0 || len(layouts) == 0 {
		return time.Now(), fmt.Errorf(
			"Requires value [%v] and at least one layout [%v]", value, strings.Join(layouts, ","))
	}
	for _, layout := range layouts {
		layout = strings.TrimSpace(layout)
		if len(layout) == 0 {
			continue
		}
		if dt, err := time.Parse(layout, value); err == nil {
			return dt, nil
		}
	}
	return time.Now(), fmt.Errorf("Cannot parse time [%v] with layouts [%v]",
		value, strings.Join(layouts, ","))
}

var FormatMap = map[string]string{
	"RFC3339":    time.RFC3339,
	"RFC3339YMD": RFC3339FullDate,
	"ISO8601YM":  ISO8601YM,
}

func GetFormat(formatName string) (string, error) {
	format, ok := FormatMap[strings.TrimSpace(formatName)]
	if !ok {
		return "", fmt.Errorf("Format Not Found: %v", format)
	}
	return format, nil
}

// FormatQuarter takes quarter time and formats it using "Q# YYYY".
func FormatQuarter(t time.Time) string {
	return fmt.Sprintf("Q%d %d", MonthToQuarter(uint8(t.Month())), t.Year())
}

func TimeRFC3339Min() time.Time {
	t0, _ := time.Parse(time.RFC3339, RFC3339Min)
	return t0
}

func TimeRFC3339Zero() time.Time {
	t0, _ := time.Parse(time.RFC3339, RFC3339Zero)
	return t0
}

func TimeRFC3339ZeroUnix() time.Time {
	t0, _ := time.Parse(time.RFC3339, RFC3339ZeroUnix)
	return t0
}

func TimeIsZeroAny(u time.Time) bool {
	if u.Equal(TimeRFC3339Zero()) ||
		u.Equal(TimeRFC3339Min()) ||
		u.Equal(TimeRFC3339ZeroUnix()) {
		return true
	}
	return false
}

type RFC3339YMDTime struct{ time.Time }

type ISO8601NoTzMilliTime struct{ time.Time }

func (t *RFC3339YMDTime) UnmarshalJSON(buf []byte) error {
	tt, isNil, err := timeUnmarshalJSON(buf, RFC3339FullDate)
	if err != nil || isNil {
		return err
	}
	t.Time = tt
	return nil
}

func (t RFC3339YMDTime) MarshalJSON() ([]byte, error) {
	return timeMarshalJSON(t.Time, RFC3339FullDate)
}

func (t *ISO8601NoTzMilliTime) UnmarshalJSON(buf []byte) error {
	tt, isNil, err := timeUnmarshalJSON(buf, ISO8601MilliNoTZ)
	if err != nil || isNil {
		return err
	}
	t.Time = tt
	return nil
}

func (t ISO8601NoTzMilliTime) MarshalJSON() ([]byte, error) {
	return timeMarshalJSON(t.Time, ISO8601MilliNoTZ)
}

func timeUnmarshalJSON(buf []byte, layout string) (time.Time, bool, error) {
	str := string(buf)
	isNil := true
	if str == "null" || str == "\"\"" {
		return time.Time{}, isNil, nil
	}
	tt, err := time.Parse(layout, strings.Trim(str, `"`))
	if err != nil {
		return time.Time{}, false, err
	}
	return tt, false, nil
}

func timeMarshalJSON(t time.Time, layout string) ([]byte, error) {
	return []byte(`"` + t.Format(layout) + `"`), nil
}
<commit_msg>add timeutil.FormatQuarterYYYYQ<commit_after>// timeutil provides a set of time utilities including comparisons,
// conversion to "DT8" int32 and "DT14" int64 formats and other
// capabilities.
package timeutil

import (
	"fmt"
	"strings"
	"time"
)

// More predefined layouts for use in Time.Format and time.Parse.
const (
	DT14               = "20060102150405"
	DT8                = "20060102"
	DT6                = "200601"
	RFC3339FullDate    = "2006-01-02"
	ISO8601            = "2006-01-02T15:04:05Z0700"
	ISO8601TZHour      = "2006-01-02T15:04:05Z07"
	ISO8601MilliNoTZ   = "2006-01-02T15:04:05.000"
	ISO8601CompactZ    = "20060102T150405Z0700"
	ISO8601CompactNoTZ = "20060102T150405"
	ISO8601YM          = "2006-01"
	InsightlyApiQuery  = "_1/_2/2006 _3:04:05 PM"
	SQLTimestamp       = "2006-01-02 15:04:05" // MySQL, BigQuery, etc.
	DateMDYSlash       = "01/02/2006"
	DateDMYHM2         = "02:01:06 15:04" // GMT time in format dd:mm:yy hh:mm
)

const (
	RFC3339Min         = "0000-01-01T00:00:00Z"
	RFC3339Max         = "9999-12-31T23:59:59Z"
	RFC3339Zero        = "0001-01-01T00:00:00Z"
	RFC3339ZeroUnix    = "1970-01-01T00:00:00Z"
	RFC3339YMDZeroUnix = int64(-62135596800)
)

// Reformat a time string from one format to another
func FromTo(value, fromLayout, toLayout string) (string, error) {
	t, err := time.Parse(fromLayout, strings.TrimSpace(value))
	if err != nil {
		return "", err
	}
	return t.Format(toLayout), nil
}

// ParseFirst attempts to parse a string with a set of layouts.
func ParseFirst(layouts []string, value string) (time.Time, error) {
	value = strings.TrimSpace(value)
	if len(value) == 0 || len(layouts) == 0 {
		return time.Now(), fmt.Errorf(
			"Requires value [%v] and at least one layout [%v]", value, strings.Join(layouts, ","))
	}
	for _, layout := range layouts {
		layout = strings.TrimSpace(layout)
		if len(layout) == 0 {
			continue
		}
		if dt, err := time.Parse(layout, value); err == nil {
			return dt, nil
		}
	}
	return time.Now(), fmt.Errorf("Cannot parse time [%v] with layouts [%v]",
		value, strings.Join(layouts, ","))
}

var FormatMap = map[string]string{
	"RFC3339":    time.RFC3339,
	"RFC3339YMD": RFC3339FullDate,
	"ISO8601YM":  ISO8601YM,
}

func GetFormat(formatName string) (string, error) {
	format, ok := FormatMap[strings.TrimSpace(formatName)]
	if !ok {
		return "", fmt.Errorf("Format Not Found: %v", format)
	}
	return format, nil
}

// FormatQuarter takes quarter time and formats it using "Q# YYYY".
func FormatQuarter(t time.Time) string {
	return fmt.Sprintf("Q%d %d", MonthToQuarter(uint8(t.Month())), t.Year())
}

// FormatQuarter takes quarter time and formats it using "Q# YYYY".
func FormatQuarterYYYYQ(t time.Time) string {
	return fmt.Sprintf("%d Q%d", t.Year(), MonthToQuarter(uint8(t.Month())))
}

func TimeRFC3339Min() time.Time {
	t0, _ := time.Parse(time.RFC3339, RFC3339Min)
	return t0
}

func TimeRFC3339Zero() time.Time {
	t0, _ := time.Parse(time.RFC3339, RFC3339Zero)
	return t0
}

func TimeRFC3339ZeroUnix() time.Time {
	t0, _ := time.Parse(time.RFC3339, RFC3339ZeroUnix)
	return t0
}

func TimeIsZeroAny(u time.Time) bool {
	if u.Equal(TimeRFC3339Zero()) ||
		u.Equal(TimeRFC3339Min()) ||
		u.Equal(TimeRFC3339ZeroUnix()) {
		return true
	}
	return false
}

type RFC3339YMDTime struct{ time.Time }

type ISO8601NoTzMilliTime struct{ time.Time }

func (t *RFC3339YMDTime) UnmarshalJSON(buf []byte) error {
	tt, isNil, err := timeUnmarshalJSON(buf, RFC3339FullDate)
	if err != nil || isNil {
		return err
	}
	t.Time = tt
	return nil
}

func (t RFC3339YMDTime) MarshalJSON() ([]byte, error) {
	return timeMarshalJSON(t.Time, RFC3339FullDate)
}

func (t *ISO8601NoTzMilliTime) UnmarshalJSON(buf []byte) error {
	tt, isNil, err := timeUnmarshalJSON(buf, ISO8601MilliNoTZ)
	if err != nil || isNil {
		return err
	}
	t.Time = tt
	return nil
}

func (t ISO8601NoTzMilliTime) MarshalJSON() ([]byte, error) {
	return timeMarshalJSON(t.Time, ISO8601MilliNoTZ)
}

func timeUnmarshalJSON(buf []byte, layout string) (time.Time, bool, error) {
	str := string(buf)
	isNil := true
	if str == "null" || str == "\"\"" {
		return time.Time{}, isNil, nil
	}
	tt, err := time.Parse(layout, strings.Trim(str, `"`))
	if err != nil {
		return time.Time{}, false, err
	}
	return tt, false, nil
}

func timeMarshalJSON(t time.Time, layout string) ([]byte, error) {
	return []byte(`"` + t.Format(layout) + `"`), nil
}
<|endoftext|>


<commit_before>//shellBars sort
package main

func shellBars(arr []int) {
	len := len(arr)
	for step := len >> 1; step > 0; step = step >> 1 {
		for i := step; i < len; i++ {
			tmp := arr[i]
			tmp_index := i
			for j := i - step; j >= 0 && arr[j] > tmp; j = j - step {
				arr[j+step] = arr[j]
				tmp_index = j
			}
			if i != tmp_index {
				arr[tmp_index] = tmp
			}
		}
	}
}
<commit_msg>代码验证希尔排序比较次数与数组长度比例是个小常数<commit_after>//shellBars sort
package main

//import "fmt"
func shellBars(arr []int) {
	len := len(arr)
	//compareNums := 0
	for step := len >> 1; step > 0; step = step >> 1 {
		for i := step; i < len; i++ {
			tmp := arr[i]
			tmp_index := i
			for j := i - step; j >= 0 && arr[j] > tmp; j = j - step {
				//compareNums++
				arr[j+step] = arr[j]
				tmp_index = j
			}
			if i != tmp_index {
				arr[tmp_index] = tmp
			}
		}
	}

	//fmt.Println(compareNums / len)
}
<|endoftext|>


<commit_before>package initialize

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	"github.com/coreos/coreos-cloudinit/system"
)

type UserKey struct {
	ID  int    `json:"id,omitempty"`
	Key string `json:"key"`
}

func SSHImportKeysFromURL(system_user string, url string) error {
	keys, err := fetchUserKeys(url)
	if err != nil {
		return err
	}

	key_name := fmt.Sprintf("coreos-cloudinit-%s", system_user)
	return system.AuthorizeSSHKeys(system_user, key_name, keys)
}

func fetchUserKeys(url string) ([]string, error) {
	res, err := http.Get(url)
	defer res.Body.Close()
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}
	var data []UserKey
	err = json.Unmarshal(body, &data)
	if err != nil {
		return nil, err
	}
	keys := make([]string, 0)
	for _, key := range data {
		keys = append(keys, key.Key)
	}
	return keys, err
}
<commit_msg>Response body must not be closed if request error'd.<commit_after>package initialize

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	"github.com/coreos/coreos-cloudinit/system"
)

type UserKey struct {
	ID  int    `json:"id,omitempty"`
	Key string `json:"key"`
}

func SSHImportKeysFromURL(system_user string, url string) error {
	keys, err := fetchUserKeys(url)
	if err != nil {
		return err
	}

	key_name := fmt.Sprintf("coreos-cloudinit-%s", system_user)
	return system.AuthorizeSSHKeys(system_user, key_name, keys)
}

func fetchUserKeys(url string) ([]string, error) {
	res, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}
	var data []UserKey
	err = json.Unmarshal(body, &data)
	if err != nil {
		return nil, err
	}
	keys := make([]string, 0)
	for _, key := range data {
		keys = append(keys, key.Key)
	}
	return keys, err
}
<|endoftext|>


<commit_before>package platform

import (
	boshdpresolv "github.com/cloudfoundry/bosh-agent/infrastructure/devicepathresolver"
	boshcert "github.com/cloudfoundry/bosh-agent/platform/cert"
	boshnet "github.com/cloudfoundry/bosh-agent/platform/net"
	boshstats "github.com/cloudfoundry/bosh-agent/platform/stats"
	boshvitals "github.com/cloudfoundry/bosh-agent/platform/vitals"
	boshsettings "github.com/cloudfoundry/bosh-agent/settings"
	boshdir "github.com/cloudfoundry/bosh-agent/settings/directories"
	boshdirs "github.com/cloudfoundry/bosh-agent/settings/directories"
	boshcmd "github.com/cloudfoundry/bosh-utils/fileutil"
	boshlog "github.com/cloudfoundry/bosh-utils/logger"
	boshsys "github.com/cloudfoundry/bosh-utils/system"
)

type WindowsPlatform struct {
	collector          boshstats.Collector
	fs                 boshsys.FileSystem
	cmdRunner          boshsys.CmdRunner
	compressor         boshcmd.Compressor
	copier             boshcmd.Copier
	dirProvider        boshdirs.Provider
	vitalsService      boshvitals.Service
	netManager         boshnet.Manager
	devicePathResolver boshdpresolv.DevicePathResolver
	certManager        boshcert.Manager
}

func NewWindowsPlatform(
	collector boshstats.Collector,
	fs boshsys.FileSystem,
	cmdRunner boshsys.CmdRunner,
	dirProvider boshdirs.Provider,
	netManager boshnet.Manager,
	devicePathResolver boshdpresolv.DevicePathResolver,
	logger boshlog.Logger,
) Platform {
	return &WindowsPlatform{
		fs:                 fs,
		cmdRunner:          cmdRunner,
		collector:          collector,
		compressor:         boshcmd.NewTarballCompressor(cmdRunner, fs),
		copier:             boshcmd.NewGenericCpCopier(fs, logger),
		dirProvider:        dirProvider,
		netManager:         netManager,
		devicePathResolver: devicePathResolver,
		vitalsService:      boshvitals.NewService(collector, dirProvider),
		certManager:        boshcert.NewDummyCertManager(fs, cmdRunner, 0, logger),
	}
}

func (p WindowsPlatform) GetFs() (fs boshsys.FileSystem) {
	return p.fs
}

func (p WindowsPlatform) GetRunner() (runner boshsys.CmdRunner) {
	return p.cmdRunner
}

func (p WindowsPlatform) GetCompressor() (compressor boshcmd.Compressor) {
	return p.compressor
}

func (p WindowsPlatform) GetCopier() (copier boshcmd.Copier) {
	return p.copier
}

func (p WindowsPlatform) GetDirProvider() (dirProvider boshdir.Provider) {
	return p.dirProvider
}

func (p WindowsPlatform) GetVitalsService() (service boshvitals.Service) {
	return p.vitalsService
}

func (p WindowsPlatform) GetDevicePathResolver() (devicePathResolver boshdpresolv.DevicePathResolver) {
	return p.devicePathResolver
}

func (p WindowsPlatform) SetupRuntimeConfiguration() (err error) {
	return
}

func (p WindowsPlatform) CreateUser(username, password, basePath string) (err error) {
	return
}

func (p WindowsPlatform) AddUserToGroups(username string, groups []string) (err error) {
	return
}

func (p WindowsPlatform) DeleteEphemeralUsersMatching(regex string) (err error) {
	return
}

func (p WindowsPlatform) SetupRootDisk(ephemeralDiskPath string) (err error) {
	return
}

func (p WindowsPlatform) SetupSSH(publicKey, username string) (err error) {
	return
}

func (p WindowsPlatform) SetUserPassword(user, encryptedPwd string) (err error) {
	return
}

func (p WindowsPlatform) SetupHostname(hostname string) (err error) {
	return
}

func (p WindowsPlatform) SetupNetworking(networks boshsettings.Networks) (err error) {
	return p.netManager.SetupNetworking(networks, nil)
}

func (p WindowsPlatform) GetConfiguredNetworkInterfaces() (interfaces []string, err error) {
	return
}

func (p WindowsPlatform) GetCertManager() (certManager boshcert.Manager) {
	return p.certManager
}

func (p WindowsPlatform) SetupLogrotate(groupName, basePath, size string) (err error) {
	return
}

func (p WindowsPlatform) SetTimeWithNtpServers(servers []string) (err error) {
	return
}

func (p WindowsPlatform) SetupEphemeralDiskWithPath(devicePath string) (err error) {
	return
}

func (p WindowsPlatform) SetupRawEphemeralDisks(devices []boshsettings.DiskSettings) (err error) {
	return
}

func (p WindowsPlatform) SetupDataDir() error {
	return nil
}

func (p WindowsPlatform) SetupTmpDir() error {
	return nil
}

func (p WindowsPlatform) MountPersistentDisk(diskSettings boshsettings.DiskSettings, mountPoint string) (err error) {
	return
}

func (p WindowsPlatform) UnmountPersistentDisk(diskSettings boshsettings.DiskSettings) (didUnmount bool, err error) {
	return
}

func (p WindowsPlatform) GetEphemeralDiskPath(diskSettings boshsettings.DiskSettings) string {
	return ""
}

func (p WindowsPlatform) GetFileContentsFromCDROM(filePath string) (contents []byte, err error) {
	return p.fs.ReadFile("D:/" + filePath)
}

func (p WindowsPlatform) GetFilesContentsFromDisk(diskPath string, fileNames []string) (contents [][]byte, err error) {
	return
}

func (p WindowsPlatform) MigratePersistentDisk(fromMountPoint, toMountPoint string) (err error) {
	return
}

func (p WindowsPlatform) IsMountPoint(path string) (string, bool, error) {
	return "", true, nil
}

func (p WindowsPlatform) IsPersistentDiskMounted(diskSettings boshsettings.DiskSettings) (bool, error) {
	return true, nil
}

func (p WindowsPlatform) IsPersistentDiskMountable(diskSettings boshsettings.DiskSettings) (bool, error) {
	return true, nil
}

func (p WindowsPlatform) StartMonit() (err error) {
	return
}

func (p WindowsPlatform) SetupMonitUser() (err error) {
	return
}

func (p WindowsPlatform) GetMonitCredentials() (username, password string, err error) {
	return
}

func (p WindowsPlatform) PrepareForNetworkingChange() error {
	return nil
}

func (p WindowsPlatform) CleanIPMacAddressCache(ip string) error {
	return nil
}

func (p WindowsPlatform) RemoveDevTools(packageFileListPath string) error {
	return nil
}

func (p WindowsPlatform) GetDefaultNetwork() (boshsettings.Network, error) {
	return boshsettings.Network{}, nil
}

func (p WindowsPlatform) GetHostPublicKey() (string, error) {
	return "", nil
}
<commit_msg>Add stub for new Platform method<commit_after>package platform

import (
	boshdpresolv "github.com/cloudfoundry/bosh-agent/infrastructure/devicepathresolver"
	boshcert "github.com/cloudfoundry/bosh-agent/platform/cert"
	boshnet "github.com/cloudfoundry/bosh-agent/platform/net"
	boshstats "github.com/cloudfoundry/bosh-agent/platform/stats"
	boshvitals "github.com/cloudfoundry/bosh-agent/platform/vitals"
	boshsettings "github.com/cloudfoundry/bosh-agent/settings"
	boshdir "github.com/cloudfoundry/bosh-agent/settings/directories"
	boshdirs "github.com/cloudfoundry/bosh-agent/settings/directories"
	boshcmd "github.com/cloudfoundry/bosh-utils/fileutil"
	boshlog "github.com/cloudfoundry/bosh-utils/logger"
	boshsys "github.com/cloudfoundry/bosh-utils/system"
)

type WindowsPlatform struct {
	collector          boshstats.Collector
	fs                 boshsys.FileSystem
	cmdRunner          boshsys.CmdRunner
	compressor         boshcmd.Compressor
	copier             boshcmd.Copier
	dirProvider        boshdirs.Provider
	vitalsService      boshvitals.Service
	netManager         boshnet.Manager
	devicePathResolver boshdpresolv.DevicePathResolver
	certManager        boshcert.Manager
}

func NewWindowsPlatform(
	collector boshstats.Collector,
	fs boshsys.FileSystem,
	cmdRunner boshsys.CmdRunner,
	dirProvider boshdirs.Provider,
	netManager boshnet.Manager,
	devicePathResolver boshdpresolv.DevicePathResolver,
	logger boshlog.Logger,
) Platform {
	return &WindowsPlatform{
		fs:                 fs,
		cmdRunner:          cmdRunner,
		collector:          collector,
		compressor:         boshcmd.NewTarballCompressor(cmdRunner, fs),
		copier:             boshcmd.NewGenericCpCopier(fs, logger),
		dirProvider:        dirProvider,
		netManager:         netManager,
		devicePathResolver: devicePathResolver,
		vitalsService:      boshvitals.NewService(collector, dirProvider),
		certManager:        boshcert.NewDummyCertManager(fs, cmdRunner, 0, logger),
	}
}

func (p WindowsPlatform) GetFs() (fs boshsys.FileSystem) {
	return p.fs
}

func (p WindowsPlatform) GetRunner() (runner boshsys.CmdRunner) {
	return p.cmdRunner
}

func (p WindowsPlatform) GetCompressor() (compressor boshcmd.Compressor) {
	return p.compressor
}

func (p WindowsPlatform) GetCopier() (copier boshcmd.Copier) {
	return p.copier
}

func (p WindowsPlatform) GetDirProvider() (dirProvider boshdir.Provider) {
	return p.dirProvider
}

func (p WindowsPlatform) GetVitalsService() (service boshvitals.Service) {
	return p.vitalsService
}

func (p WindowsPlatform) GetDevicePathResolver() (devicePathResolver boshdpresolv.DevicePathResolver) {
	return p.devicePathResolver
}

func (p WindowsPlatform) SetupRuntimeConfiguration() (err error) {
	return
}

func (p WindowsPlatform) CreateUser(username, password, basePath string) (err error) {
	return
}

func (p WindowsPlatform) AddUserToGroups(username string, groups []string) (err error) {
	return
}

func (p WindowsPlatform) DeleteEphemeralUsersMatching(regex string) (err error) {
	return
}

func (p WindowsPlatform) SetupRootDisk(ephemeralDiskPath string) (err error) {
	return
}

func (p WindowsPlatform) SetupSSH(publicKey, username string) (err error) {
	return
}

func (p WindowsPlatform) SetUserPassword(user, encryptedPwd string) (err error) {
	return
}

func (p WindowsPlatform) SetupHostname(hostname string) (err error) {
	return
}

func (p WindowsPlatform) SetupNetworking(networks boshsettings.Networks) (err error) {
	return p.netManager.SetupNetworking(networks, nil)
}

func (p WindowsPlatform) GetConfiguredNetworkInterfaces() (interfaces []string, err error) {
	return
}

func (p WindowsPlatform) GetCertManager() (certManager boshcert.Manager) {
	return p.certManager
}

func (p WindowsPlatform) SetupLogrotate(groupName, basePath, size string) (err error) {
	return
}

func (p WindowsPlatform) SetTimeWithNtpServers(servers []string) (err error) {
	return
}

func (p WindowsPlatform) SetupEphemeralDiskWithPath(devicePath string) (err error) {
	return
}

func (p WindowsPlatform) SetupRawEphemeralDisks(devices []boshsettings.DiskSettings) (err error) {
	return
}

func (p WindowsPlatform) SetupDataDir() error {
	return nil
}

func (p WindowsPlatform) SetupTmpDir() error {
	return nil
}

func (p WindowsPlatform) MountPersistentDisk(diskSettings boshsettings.DiskSettings, mountPoint string) (err error) {
	return
}

func (p WindowsPlatform) UnmountPersistentDisk(diskSettings boshsettings.DiskSettings) (didUnmount bool, err error) {
	return
}

func (p WindowsPlatform) GetEphemeralDiskPath(diskSettings boshsettings.DiskSettings) string {
	return ""
}

func (p WindowsPlatform) GetFileContentsFromCDROM(filePath string) (contents []byte, err error) {
	return p.fs.ReadFile("D:/" + filePath)
}

func (p WindowsPlatform) GetFilesContentsFromDisk(diskPath string, fileNames []string) (contents [][]byte, err error) {
	return
}

func (p WindowsPlatform) MigratePersistentDisk(fromMountPoint, toMountPoint string) (err error) {
	return
}

func (p WindowsPlatform) IsMountPoint(path string) (string, bool, error) {
	return "", true, nil
}

func (p WindowsPlatform) IsPersistentDiskMounted(diskSettings boshsettings.DiskSettings) (bool, error) {
	return true, nil
}

func (p WindowsPlatform) IsPersistentDiskMountable(diskSettings boshsettings.DiskSettings) (bool, error) {
	return true, nil
}

func (p WindowsPlatform) StartMonit() (err error) {
	return
}

func (p WindowsPlatform) SetupMonitUser() (err error) {
	return
}

func (p WindowsPlatform) GetMonitCredentials() (username, password string, err error) {
	return
}

func (p WindowsPlatform) PrepareForNetworkingChange() error {
	return nil
}

func (p WindowsPlatform) CleanIPMacAddressCache(ip string) error {
	return nil
}

func (p WindowsPlatform) RemoveDevTools(packageFileListPath string) error {
	return nil
}

func (p WindowsPlatform) GetDefaultNetwork() (boshsettings.Network, error) {
	return boshsettings.Network{}, nil
}

func (p WindowsPlatform) GetHostPublicKey() (string, error) {
	return "", nil
}

func (p WindowsPlatform) DeleteARPEntryWithIP(ip string) error {
	return nil
}
<|endoftext|>


<commit_before>package forward

import (
	"errors"
	"net"

	"github.com/Microsoft/go-winio"
	"github.com/moby/vpnkit/go/pkg/libproxy"
	"github.com/moby/vpnkit/go/pkg/vpnkit"
)

// UnixNetwork specifies common parameters for Windows named pipe forwards.
type UnixNetwork struct{}

func (t UnixNetwork) listen(port vpnkit.Port) (listener, error) {
	l, err := winio.ListenPipe(port.OutPath, &winio.PipeConfig{
		MessageMode:      true,  // Use message mode so that CloseWrite() is supported
		InputBufferSize:  65536, // Use 64KB buffers to improve performance
		OutputBufferSize: 65536,
	})
	if err != nil {
		return nil, err
	}
	wrapped := unixListener{l}
	return &wrapped, nil
}

type unixListener struct {
	l net.Listener
}

func (l unixListener) accept() (libproxy.Conn, error) {
	c, err := l.l.Accept()
	if err != nil {
		return nil, err
	}
	conn, ok := c.(libproxy.Conn)
	if !ok {
		return nil, errors.New("Named pipe connection does not support WriteClose")
	}
	return conn, nil
}

func (l unixListener) close() error {
	return l.l.Close()
}

func makeUnix(c common, n UnixNetwork) (Forward, error) {
	return makeStream(c, n)
}
<commit_msg>go: allow the SecurityDescriptor to be customised<commit_after>package forward

import (
	"errors"
	"net"

	"github.com/Microsoft/go-winio"
	"github.com/moby/vpnkit/go/pkg/libproxy"
	"github.com/moby/vpnkit/go/pkg/vpnkit"
)

// UnixNetwork specifies common parameters for Windows named pipe forwards.
type UnixNetwork struct {
	SecurityDescriptor string // SecurityDescriptor will apply to all the pipes.
}

func (t UnixNetwork) listen(port vpnkit.Port) (listener, error) {
	l, err := winio.ListenPipe(port.OutPath, &winio.PipeConfig{
		SecurityDescriptor: t.SecurityDescriptor,
		MessageMode:        true,  // Use message mode so that CloseWrite() is supported
		InputBufferSize:    65536, // Use 64KB buffers to improve performance
		OutputBufferSize:   65536,
	})
	if err != nil {
		return nil, err
	}
	wrapped := unixListener{l}
	return &wrapped, nil
}

type unixListener struct {
	l net.Listener
}

func (l unixListener) accept() (libproxy.Conn, error) {
	c, err := l.l.Accept()
	if err != nil {
		return nil, err
	}
	conn, ok := c.(libproxy.Conn)
	if !ok {
		return nil, errors.New("Named pipe connection does not support WriteClose")
	}
	return conn, nil
}

func (l unixListener) close() error {
	return l.l.Close()
}

func makeUnix(c common, n UnixNetwork) (Forward, error) {
	return makeStream(c, n)
}
<|endoftext|>


<commit_before>package main

import "encoding/json"
import "flag"
import "io/ioutil"
import "log"
import "net/http"
import "net/url"
import "strings"
import "golang.org/x/net/context"
import "google.golang.org/grpc"
import pb "github.com/brotherlogic/cardserver/card"

func processAccessCode(client string, secret string, code string) []byte {
	urlv := "https://api.instagram.com/oauth/access_token"
	resp, err := http.PostForm(urlv, url.Values{"client_id": {client}, "client_secret": {secret}, "grant_type": {"authorization_code"}, "redirect_uri": {"http://localhost:8090"}, "code": {code}, "scope": {"public_content+likes"}})
	if err != nil {
		panic(err)
	}

	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)

	return body
}

func processInstagramRating(card pb.Card) string {
	pictureID := card.Text
	urlv := "https://api.instagram.com/v1/media/" + pictureID + "/likes"
	return urlv
}

func visitURL(urlv string, accessToken string) []byte {
	resp, err := http.PostForm(urlv, url.Values{"access_token": {accessToken}})

	if err != nil {
		panic(err)
	}

	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)

	return body
}

func readCards(client string, secret string) {
	conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()
	clientReader := pb.NewCardServiceClient(conn)
	list, err := clientReader.GetCards(context.Background(), &pb.Empty{})

	var accessCode []byte
	for _, card := range list.Cards {
		if card.Hash == "instagramauthresp" {
			code := card.Text[11:43]
			accessCode = processAccessCode(client, secret, code)
		}
	}

	//Write the access card out to a file
	if len(accessCode) > 0 {
		err = ioutil.WriteFile("access_code", accessCode, 0644)
		if err != nil {
			panic(err)
		}

		conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())

		defer conn.Close()
		client := pb.NewCardServiceClient(conn)
		req := pb.DeleteRequest{Hash: "instagramauthresp"}
		_, err = client.DeleteCards(context.Background(), &req)
		if err != nil {
			log.Printf("Problem delete auth resp card %v", err)
		}

	}
}

func writeAuthCard(client string) pb.CardList {
	authURL := "https://api.instagram.com/oauth/authorize/?client_id=CLIENT-ID&redirect_uri=http://localhost:8090&response_type=code"
	newAuthURL := strings.Replace(authURL, "CLIENT-ID", client, 1)
	card := pb.Card{}
	card.Text = newAuthURL
	card.Action = pb.Card_VISITURL
	card.Hash = "instagramauth"
	cards := pb.CardList{}
	cards.Cards = append(cards.Cards, &card)

	return cards
}

func isImageLiked(mediaID string, accessToken string) bool {
	likesURL := "https://api.instagram.com/v1/media/" + mediaID + "/likes?access_token=" + accessToken
	resp, err := http.Get(likesURL)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}
	var dat map[string]interface{}
	err = json.Unmarshal([]byte(body), &dat)
	if err != nil {
		panic(nil)
	}

	var likes []interface{}
	likes = dat["data"].([]interface{})
	for _, persono := range likes {
		person := persono.(map[string]interface{})
		if person["username"].(string) == "brotherlogic" {
			return true
		}
	}

	return false
}

func writeInstagramCards(user string, accessCode string) pb.CardList {

	cards := pb.CardList{}

	urlv := "https://api.instagram.com/v1/users/" + user + "/media/recent/?access_token=" + accessCode + "&count=10"
	resp, err := http.Get(urlv)
	if err != nil {
		panic(nil)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		panic(nil)
	}
	var dat map[string]interface{}
	err = json.Unmarshal([]byte(body), &dat)
	if err != nil {
		panic(nil)
	}

	var pics []interface{}
	pics = dat["data"].([]interface{})

	for _, pico := range pics {
		pic := pico.(map[string]interface{})
		captionObj := pic["caption"]
		caption := ""
		if captionObj != nil {
			caption = captionObj.(map[string]interface{})["text"].(string)
		}
		var images map[string]interface{}
		images = pic["images"].(map[string]interface{})
		image := images["standard_resolution"].(map[string]interface{})["url"].(string)

		liked := isImageLiked(pic["id"].(string), accessCode)

		log.Printf("%v -> %v", pic["id"].(string), liked)
		if !liked {
			card := pb.Card{}
			card.Text = caption
			card.Image = image
			card.Priority = 20
			card.Hash = pic["id"].(string)
			cards.Cards = append(cards.Cards, &card)
		}
	}
	return cards
}

func main() {
	var clientID = flag.String("client", "", "Client ID for accessing Instagram")
	var secret = flag.String("secret", "", "Secret for accessing Instagram")
	flag.Parse()
	text, err := ioutil.ReadFile("access_code")

	if err != nil {
		cards := writeAuthCard(*clientID)
		conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())

		defer conn.Close()
		client := pb.NewCardServiceClient(conn)
		_, err = client.AddCards(context.Background(), &cards)
		if err != nil {
			log.Printf("Problem adding cards %v", err)
		}

		readCards(*clientID, *secret)
	} else {
		var dat map[string]interface{}
		if err := json.Unmarshal([]byte(text), &dat); err != nil {
			log.Printf("Error unmarshalling %v with %v and %v", string(text), *clientID, *secret)
			panic(err)
		}
		if dat == nil || dat["access_token"] == nil {
			log.Printf("Cannot get access token: %v from %v given $v,%v", dat, string(text), *clientID, *secret)
		}
		cards := writeInstagramCards("50987102", dat["access_token"].(string))
		conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())

		defer conn.Close()
		client := pb.NewCardServiceClient(conn)
		_, err = client.AddCards(context.Background(), &cards)
		if err != nil {
			log.Printf("Problem adding cards %v", err)
		}

	}
}
<commit_msg>Reverted logging<commit_after>package main

import "encoding/json"
import "flag"
import "io/ioutil"
import "log"
import "net/http"
import "net/url"
import "strings"
import "golang.org/x/net/context"
import "google.golang.org/grpc"
import pb "github.com/brotherlogic/cardserver/card"

func processAccessCode(client string, secret string, code string) []byte {
	urlv := "https://api.instagram.com/oauth/access_token"
	resp, err := http.PostForm(urlv, url.Values{"client_id": {client}, "client_secret": {secret}, "grant_type": {"authorization_code"}, "redirect_uri": {"http://localhost:8090"}, "code": {code}, "scope": {"public_content+likes"}})
	if err != nil {
		panic(err)
	}

	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)

	return body
}

func processInstagramRating(card pb.Card) string {
	pictureID := card.Text
	urlv := "https://api.instagram.com/v1/media/" + pictureID + "/likes"
	return urlv
}

func visitURL(urlv string, accessToken string) []byte {
	resp, err := http.PostForm(urlv, url.Values{"access_token": {accessToken}})

	if err != nil {
		panic(err)
	}

	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)

	return body
}

func readCards(client string, secret string) {
	conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()
	clientReader := pb.NewCardServiceClient(conn)
	list, err := clientReader.GetCards(context.Background(), &pb.Empty{})

	var accessCode []byte
	for _, card := range list.Cards {
		if card.Hash == "instagramauthresp" {
			code := card.Text[11:43]
			accessCode = processAccessCode(client, secret, code)
		}
	}

	//Write the access card out to a file
	if len(accessCode) > 0 {
		err = ioutil.WriteFile("access_code", accessCode, 0644)
		if err != nil {
			panic(err)
		}

		conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())

		defer conn.Close()
		client := pb.NewCardServiceClient(conn)
		req := pb.DeleteRequest{Hash: "instagramauthresp"}
		_, err = client.DeleteCards(context.Background(), &req)
		if err != nil {
			log.Printf("Problem delete auth resp card %v", err)
		}

	}
}

func writeAuthCard(client string) pb.CardList {
	authURL := "https://api.instagram.com/oauth/authorize/?client_id=CLIENT-ID&redirect_uri=http://localhost:8090&response_type=code"
	newAuthURL := strings.Replace(authURL, "CLIENT-ID", client, 1)
	card := pb.Card{}
	card.Text = newAuthURL
	card.Action = pb.Card_VISITURL
	card.Hash = "instagramauth"
	cards := pb.CardList{}
	cards.Cards = append(cards.Cards, &card)

	return cards
}

func isImageLiked(mediaID string, accessToken string) bool {
	likesURL := "https://api.instagram.com/v1/media/" + mediaID + "/likes?access_token=" + accessToken
	resp, err := http.Get(likesURL)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}
	var dat map[string]interface{}
	err = json.Unmarshal([]byte(body), &dat)
	if err != nil {
		panic(nil)
	}

	var likes []interface{}
	likes = dat["data"].([]interface{})
	for _, persono := range likes {
		person := persono.(map[string]interface{})
		if person["username"].(string) == "brotherlogic" {
			return true
		}
	}

	return false
}

func writeInstagramCards(user string, accessCode string) pb.CardList {

	cards := pb.CardList{}

	urlv := "https://api.instagram.com/v1/users/" + user + "/media/recent/?access_token=" + accessCode + "&count=10"
	resp, err := http.Get(urlv)
	if err != nil {
		panic(nil)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		panic(nil)
	}
	var dat map[string]interface{}
	err = json.Unmarshal([]byte(body), &dat)
	if err != nil {
		panic(nil)
	}

	var pics []interface{}
	pics = dat["data"].([]interface{})

	for _, pico := range pics {
		pic := pico.(map[string]interface{})
		captionObj := pic["caption"]
		caption := ""
		if captionObj != nil {
			caption = captionObj.(map[string]interface{})["text"].(string)
		}
		var images map[string]interface{}
		images = pic["images"].(map[string]interface{})
		image := images["standard_resolution"].(map[string]interface{})["url"].(string)

		liked := isImageLiked(pic["id"].(string), accessCode)

		if !liked {
			card := pb.Card{}
			card.Text = caption
			card.Image = image
			card.Priority = 20
			card.Hash = pic["id"].(string)
			cards.Cards = append(cards.Cards, &card)
		}
	}
	return cards
}

func main() {
	var clientID = flag.String("client", "", "Client ID for accessing Instagram")
	var secret = flag.String("secret", "", "Secret for accessing Instagram")
	flag.Parse()
	text, err := ioutil.ReadFile("access_code")

	if err != nil {
		cards := writeAuthCard(*clientID)
		conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())

		defer conn.Close()
		client := pb.NewCardServiceClient(conn)
		_, err = client.AddCards(context.Background(), &cards)
		if err != nil {
			log.Printf("Problem adding cards %v", err)
		}

		readCards(*clientID, *secret)
	} else {
		var dat map[string]interface{}
		if err := json.Unmarshal([]byte(text), &dat); err != nil {
			log.Printf("Error unmarshalling %v with %v and %v", string(text), *clientID, *secret)
			panic(err)
		}
		if dat == nil || dat["access_token"] == nil {
			log.Printf("Cannot get access token: %v from %v given $v,%v", dat, string(text), *clientID, *secret)
		}
		cards := writeInstagramCards("50987102", dat["access_token"].(string))
		conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())

		defer conn.Close()
		client := pb.NewCardServiceClient(conn)
		_, err = client.AddCards(context.Background(), &cards)
		if err != nil {
			log.Printf("Problem adding cards %v", err)
		}

	}
}
<|endoftext|>


<commit_before>/* Goiardi configuration. */

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

package config

import (
	"github.com/jessevdk/go-flags"
	"github.com/BurntSushi/toml"
	"os"
	"log"
	"fmt"
	"time"
	"path"
)

/* Master struct for configuration. */
type Conf struct {
	Ipaddress string
	Port int
	Hostname string
	ConfFile string
	IndexFile string
	DataStoreFile string
	DebugLevel int
	FreezeInterval int
	FreezeData bool
	LogFile string
	UseAuth bool
	TimeSlew string
	TimeSlewDur time.Duration
	ConfRoot string
	UseSSL bool
	SslCert string
	SslKey string
	HttpsUrls bool
}

/* Struct for command line options. */
type Options struct {
	Version bool `short:"v" long:"version" description:"Print version info."`
	Verbose []bool `short:"V" long:"verbose" description:"Show verbose debug information. (not implemented)"`
	ConfFile string `short:"c" long:"config" description:"Specify a config file to use."`
	Ipaddress string `short:"I" long:"ipaddress" description:"Listen on a specific IP address."`
	Hostname string `short:"H" long:"hostname" description:"Hostname to use for this server. Defaults to hostname reported by the kernel."`
	Port int `short:"P" long:"port" description:"Port to listen on. If port is set to 443, SSL will be activated. (default: 4545)"`
	IndexFile string `short:"i" long:"index-file" description:"File to save search index data to."`
	DataStoreFile string `short:"D" long:"data-file" description:"File to save data store data to."`
	FreezeInterval int `short:"F" long:"freeze-interval" description:"Interval in seconds to freeze in-memory data structures to disk (requires -i/--index-file and -D/--data-file options to be set). (Default 300 seconds/5 minutes.)"`
	LogFile string `short:"L" long:"log-file" description:"Log to file X"`
	TimeSlew string `long:"time-slew" description:"Time difference allowed between the server's clock at the time in the X-OPS-TIMESTAMP header. Formatted like 5m, 150s, etc. Defaults to 15m."`
	ConfRoot string `long:"conf-root" description:"Root directory for configs and certificates. Default: /etc/goiardi, or the directory the config file is in."`
	UseAuth bool `short:"A" long:"use-auth" description:"Use authentication. Default: false."`
	UseSSL bool `long:"use-ssl" description:"Use SSL for connections. If --port is set to 433, this will automatically be turned on. If it is set to 80, it will automatically be turned off. Default: off. Requires --ssl-cert and --ssl-key."`
	SslCert string `long:"ssl-cert" description:"SSL certificate file."`
	SslKey string `long:"ssl-key" description:"SSL key file."`
	HttpsUrls bool `long:"https-urls" description:"Use 'https://' in URLs to server resources if goiardi is not using SSL for its connections. Useful when goiardi is sitting behind a reverse proxy that uses SSL, but is communicating with the proxy over HTTP."`
}

// The goiardi version
const Version = "0.3.0"
// The chef version we're at least aiming for, even if it's not complete yet
const ChefVersion = "11.0.8"

/* The general plan is to read the command-line options, then parse the config
 * file, fill in the config struct with those values, then apply the
 * command-line options to the config struct. We read the cli options first so
 * we know to look for a different config file if needed, but otherwise the
 * command line options override what's in the config file. */

func InitConfig() *Conf { return &Conf{ } }

var Config = InitConfig()

// Read and apply arguments from the command line.
func ParseConfigOptions() error {
	var opts = &Options{ }
	_, err := flags.Parse(opts)

	if err != nil {
		if err.(*flags.Error).Type == flags.ErrHelp {
			os.Exit(0)
		} else {
			log.Println(err)
			os.Exit(1)
		}
	}

	if opts.Version {
		fmt.Printf("goiardi version %s (aiming for compatibility with Chef Server version %s).\n", Version, ChefVersion)
		os.Exit(0)
	}

	/* Load the config file. Command-line options have precedence over
	 * config file options. */
	if opts.ConfFile != "" {
		if _, err := toml.DecodeFile(opts.ConfFile, Config); err != nil {
			panic(err)
			os.Exit(1)
		}
		Config.FreezeData = false
	}

	if opts.Hostname != "" {
		Config.Hostname = opts.Hostname
	} else {
		if Config.Hostname == "" {
			Config.Hostname, err = os.Hostname()
			if err != nil {
				log.Println(err)
				Config.Hostname = "localhost"
			}
		}
	}

	if !((opts.DataStoreFile == "" && opts.IndexFile == "") || (opts.DataStoreFile != "" && opts.IndexFile != "")) {
		err := fmt.Errorf("-i and -D must either both be specified, or not specified.")
		panic(err)
		os.Exit(1)
	}
	if opts.DataStoreFile != "" {
		Config.DataStoreFile = opts.DataStoreFile
		Config.FreezeData = true
	}

	if opts.IndexFile != "" {
		Config.IndexFile = opts.IndexFile
		Config.FreezeData = true
	}

	if opts.LogFile != "" {
		Config.LogFile = opts.LogFile
	}
	if Config.LogFile != "" {
		lfp, lerr := os.Create(Config.LogFile)
		if lerr != nil {
			log.Println(err)
			os.Exit(1)
		}
		log.SetOutput(lfp)
	}

	if !Config.FreezeData && (opts.FreezeInterval != 0 || Config.FreezeInterval != 0) {
		log.Printf("FYI, setting the freeze data interval's not especially useful without setting the index and data files.")
	}
	if opts.FreezeInterval != 0 {
		Config.FreezeInterval = opts.FreezeInterval
	}
	if Config.FreezeInterval == 0 {
		Config.FreezeInterval = 300
	}

	Config.Ipaddress = opts.Ipaddress
	if opts.Port != 0 {
		Config.Port = opts.Port
	}
	if Config.Port == 0 {
		Config.Port = 4545
	}

	if opts.UseSSL {
		Config.UseSSL = opts.UseSSL
	}
	if opts.SslCert != "" {
		Config.SslCert = opts.SslCert
	}
	if opts.SslKey != "" {
		Config.SslKey = opts.SslKey
	}
	if opts.HttpsUrls {
		Config.HttpsUrls = opts.HttpsUrls
	}
	// SSL setup
	if Config.Port == 80 {
		Config.UseSSL = false
	} else if Config.Port == 443 {
		Config.UseSSL = true
	}
	if Config.UseSSL {
		if Config.SslCert == "" || Config.SslKey == "" {
			log.Println("SSL mode requires specifying both a certificate and a key file.")
			os.Exit(1)
		}
	}

	Config.DebugLevel = len(opts.Verbose)

	if opts.TimeSlew != "" {
		Config.TimeSlew = opts.TimeSlew
	}
	if Config.TimeSlew != "" {
		d, derr := time.ParseDuration(Config.TimeSlew)
		if derr != nil {
			log.Println("Error parsing time-slew:", derr)
			os.Exit(1)
		}
		Config.TimeSlewDur = d
	} else {
		Config.TimeSlewDur, _ = time.ParseDuration("15m")
	}

	/* config root - make this a real option */
	if opts.ConfRoot != "" {
		Config.ConfRoot = opts.ConfRoot
	}
	if Config.ConfRoot == "" {
		if Config.ConfFile != "" {
			Config.ConfRoot = path.Dir(Config.ConfFile)
		} else {
			Config.ConfRoot = "."
		}
	}

	if opts.UseAuth {
		Config.UseAuth = opts.UseAuth
	}

	return nil
}

// The address and port goiardi is configured to listen on.
func ListenAddr() string {
	listen_addr := fmt.Sprintf("%s:%d", Config.Ipaddress, Config.Port)
	return listen_addr
}

// The hostname and port goiardi is configured to use.
func ServerHostname() string {
	var portStr string
	if !(Config.Port == 80 || Config.Port == 443) {
		portStr = fmt.Sprintf(":%d", Config.Port)
	}
	hostname := fmt.Sprintf("%s%s", Config.Hostname, portStr)
	return hostname
}

// The base URL
func ServerBaseURL() string {
	/* TODO: allow configuring using http vs. https */
	var urlScheme string
	if Config.UseSSL || Config.HttpsUrls {
		urlScheme = "https"
	} else {
		urlScheme = "http"
	}
	url := fmt.Sprintf("%s://%s", urlScheme, ServerHostname())
	return url
}
<commit_msg>Bump version number in preparation for a release<commit_after>/* Goiardi configuration. */

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

package config

import (
	"github.com/jessevdk/go-flags"
	"github.com/BurntSushi/toml"
	"os"
	"log"
	"fmt"
	"time"
	"path"
)

/* Master struct for configuration. */
type Conf struct {
	Ipaddress string
	Port int
	Hostname string
	ConfFile string
	IndexFile string
	DataStoreFile string
	DebugLevel int
	FreezeInterval int
	FreezeData bool
	LogFile string
	UseAuth bool
	TimeSlew string
	TimeSlewDur time.Duration
	ConfRoot string
	UseSSL bool
	SslCert string
	SslKey string
	HttpsUrls bool
}

/* Struct for command line options. */
type Options struct {
	Version bool `short:"v" long:"version" description:"Print version info."`
	Verbose []bool `short:"V" long:"verbose" description:"Show verbose debug information. (not implemented)"`
	ConfFile string `short:"c" long:"config" description:"Specify a config file to use."`
	Ipaddress string `short:"I" long:"ipaddress" description:"Listen on a specific IP address."`
	Hostname string `short:"H" long:"hostname" description:"Hostname to use for this server. Defaults to hostname reported by the kernel."`
	Port int `short:"P" long:"port" description:"Port to listen on. If port is set to 443, SSL will be activated. (default: 4545)"`
	IndexFile string `short:"i" long:"index-file" description:"File to save search index data to."`
	DataStoreFile string `short:"D" long:"data-file" description:"File to save data store data to."`
	FreezeInterval int `short:"F" long:"freeze-interval" description:"Interval in seconds to freeze in-memory data structures to disk (requires -i/--index-file and -D/--data-file options to be set). (Default 300 seconds/5 minutes.)"`
	LogFile string `short:"L" long:"log-file" description:"Log to file X"`
	TimeSlew string `long:"time-slew" description:"Time difference allowed between the server's clock at the time in the X-OPS-TIMESTAMP header. Formatted like 5m, 150s, etc. Defaults to 15m."`
	ConfRoot string `long:"conf-root" description:"Root directory for configs and certificates. Default: /etc/goiardi, or the directory the config file is in."`
	UseAuth bool `short:"A" long:"use-auth" description:"Use authentication. Default: false."`
	UseSSL bool `long:"use-ssl" description:"Use SSL for connections. If --port is set to 433, this will automatically be turned on. If it is set to 80, it will automatically be turned off. Default: off. Requires --ssl-cert and --ssl-key."`
	SslCert string `long:"ssl-cert" description:"SSL certificate file."`
	SslKey string `long:"ssl-key" description:"SSL key file."`
	HttpsUrls bool `long:"https-urls" description:"Use 'https://' in URLs to server resources if goiardi is not using SSL for its connections. Useful when goiardi is sitting behind a reverse proxy that uses SSL, but is communicating with the proxy over HTTP."`
}

// The goiardi version
const Version = "0.4.0"
// The chef version we're at least aiming for, even if it's not complete yet
const ChefVersion = "11.0.8"

/* The general plan is to read the command-line options, then parse the config
 * file, fill in the config struct with those values, then apply the
 * command-line options to the config struct. We read the cli options first so
 * we know to look for a different config file if needed, but otherwise the
 * command line options override what's in the config file. */

func InitConfig() *Conf { return &Conf{ } }

var Config = InitConfig()

// Read and apply arguments from the command line.
func ParseConfigOptions() error {
	var opts = &Options{ }
	_, err := flags.Parse(opts)

	if err != nil {
		if err.(*flags.Error).Type == flags.ErrHelp {
			os.Exit(0)
		} else {
			log.Println(err)
			os.Exit(1)
		}
	}

	if opts.Version {
		fmt.Printf("goiardi version %s (aiming for compatibility with Chef Server version %s).\n", Version, ChefVersion)
		os.Exit(0)
	}

	/* Load the config file. Command-line options have precedence over
	 * config file options. */
	if opts.ConfFile != "" {
		if _, err := toml.DecodeFile(opts.ConfFile, Config); err != nil {
			panic(err)
			os.Exit(1)
		}
		Config.FreezeData = false
	}

	if opts.Hostname != "" {
		Config.Hostname = opts.Hostname
	} else {
		if Config.Hostname == "" {
			Config.Hostname, err = os.Hostname()
			if err != nil {
				log.Println(err)
				Config.Hostname = "localhost"
			}
		}
	}

	if !((opts.DataStoreFile == "" && opts.IndexFile == "") || (opts.DataStoreFile != "" && opts.IndexFile != "")) {
		err := fmt.Errorf("-i and -D must either both be specified, or not specified.")
		panic(err)
		os.Exit(1)
	}
	if opts.DataStoreFile != "" {
		Config.DataStoreFile = opts.DataStoreFile
		Config.FreezeData = true
	}

	if opts.IndexFile != "" {
		Config.IndexFile = opts.IndexFile
		Config.FreezeData = true
	}

	if opts.LogFile != "" {
		Config.LogFile = opts.LogFile
	}
	if Config.LogFile != "" {
		lfp, lerr := os.Create(Config.LogFile)
		if lerr != nil {
			log.Println(err)
			os.Exit(1)
		}
		log.SetOutput(lfp)
	}

	if !Config.FreezeData && (opts.FreezeInterval != 0 || Config.FreezeInterval != 0) {
		log.Printf("FYI, setting the freeze data interval's not especially useful without setting the index and data files.")
	}
	if opts.FreezeInterval != 0 {
		Config.FreezeInterval = opts.FreezeInterval
	}
	if Config.FreezeInterval == 0 {
		Config.FreezeInterval = 300
	}

	Config.Ipaddress = opts.Ipaddress
	if opts.Port != 0 {
		Config.Port = opts.Port
	}
	if Config.Port == 0 {
		Config.Port = 4545
	}

	if opts.UseSSL {
		Config.UseSSL = opts.UseSSL
	}
	if opts.SslCert != "" {
		Config.SslCert = opts.SslCert
	}
	if opts.SslKey != "" {
		Config.SslKey = opts.SslKey
	}
	if opts.HttpsUrls {
		Config.HttpsUrls = opts.HttpsUrls
	}
	// SSL setup
	if Config.Port == 80 {
		Config.UseSSL = false
	} else if Config.Port == 443 {
		Config.UseSSL = true
	}
	if Config.UseSSL {
		if Config.SslCert == "" || Config.SslKey == "" {
			log.Println("SSL mode requires specifying both a certificate and a key file.")
			os.Exit(1)
		}
	}

	Config.DebugLevel = len(opts.Verbose)

	if opts.TimeSlew != "" {
		Config.TimeSlew = opts.TimeSlew
	}
	if Config.TimeSlew != "" {
		d, derr := time.ParseDuration(Config.TimeSlew)
		if derr != nil {
			log.Println("Error parsing time-slew:", derr)
			os.Exit(1)
		}
		Config.TimeSlewDur = d
	} else {
		Config.TimeSlewDur, _ = time.ParseDuration("15m")
	}

	/* config root - make this a real option */
	if opts.ConfRoot != "" {
		Config.ConfRoot = opts.ConfRoot
	}
	if Config.ConfRoot == "" {
		if Config.ConfFile != "" {
			Config.ConfRoot = path.Dir(Config.ConfFile)
		} else {
			Config.ConfRoot = "."
		}
	}

	if opts.UseAuth {
		Config.UseAuth = opts.UseAuth
	}

	return nil
}

// The address and port goiardi is configured to listen on.
func ListenAddr() string {
	listen_addr := fmt.Sprintf("%s:%d", Config.Ipaddress, Config.Port)
	return listen_addr
}

// The hostname and port goiardi is configured to use.
func ServerHostname() string {
	var portStr string
	if !(Config.Port == 80 || Config.Port == 443) {
		portStr = fmt.Sprintf(":%d", Config.Port)
	}
	hostname := fmt.Sprintf("%s%s", Config.Hostname, portStr)
	return hostname
}

// The base URL
func ServerBaseURL() string {
	/* TODO: allow configuring using http vs. https */
	var urlScheme string
	if Config.UseSSL || Config.HttpsUrls {
		urlScheme = "https"
	} else {
		urlScheme = "http"
	}
	url := fmt.Sprintf("%s://%s", urlScheme, ServerHostname())
	return url
}
<|endoftext|>