<commit_before>/*
Copyright 2013 The Camlistore Authors

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

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

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

// Packaage context provides a Context type to propagate state and cancellation
// information.
package context

import (
	"errors"
	"sync"
)

// ErrCanceled may be returned by code when it receives from a Context.Done channel.
var ErrCanceled = errors.New("canceled")

// TODO returns a dummy context. It's a signal that the caller code is not yet correct,
// and needs its own context to propagate.
func TODO() *Context {
	return nil
}

// A Context is carries state and cancellation information between calls.
// A nil Context pointer is valid, for now.
type Context struct {
	cancelOnce sync.Once
	done       chan struct{}
}

// New returns a new Context.
func New() *Context {
	return &Context{
		done: make(chan struct{}),
	}
}

// New returns a child context attached to the receiver parent context c.
// The returned context is done when the parent is done, but the returned child
// context can be canceled indepedently without affecting the parent.
func (c *Context) New() *Context {
	subc := New()
	if c == nil {
		return subc
	}
	go func() {
		<-c.Done()
		subc.Cancel()
	}()
	return subc
}

// Done returns a channel that is closed when the Context is cancelled
// or finished.
func (c *Context) Done() <-chan struct{} {
	if c == nil {
		return nil
	}
	return c.done
}

// IsCanceled reports whether this context has been canceled.
func (c *Context) IsCanceled() bool {
	select {
	case <-c.Done():
		return true
	default:
		return false
	}
}

// Cancel cancels the context. It is idempotent.
func (c *Context) Cancel() {
	if c == nil {
		return
	}
	c.cancelOnce.Do(c.cancel)
}

func (c *Context) cancel() {
	if c.done != nil {
		close(c.done)
	}
}
<commit_msg>context: add method to get an HTTPClient.<commit_after>/*
Copyright 2013 The Camlistore Authors

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

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

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

// Packaage context provides a Context type to propagate state and cancellation
// information.
package context

import (
	"errors"
	"net/http"
	"sync"
)

// ErrCanceled may be returned by code when it receives from a Context.Done channel.
var ErrCanceled = errors.New("canceled")

// TODO returns a dummy context. It's a signal that the caller code is not yet correct,
// and needs its own context to propagate.
func TODO() *Context {
	return nil
}

// A Context is carries state and cancellation information between calls.
// A nil Context pointer is valid, for now.
type Context struct {
	cancelOnce sync.Once
	done       chan struct{}
}

// New returns a new Context.
func New() *Context {
	return &Context{
		done: make(chan struct{}),
	}
}

// HTTPClient returns the HTTP Client to use for this context.
func (c *Context) HTTPClient() *http.Client {
	// TODO: add hook for appengine to register to supply an alternate one.
	return http.DefaultClient
}

// New returns a child context attached to the receiver parent context c.
// The returned context is done when the parent is done, but the returned child
// context can be canceled indepedently without affecting the parent.
func (c *Context) New() *Context {
	subc := New()
	if c == nil {
		return subc
	}
	go func() {
		<-c.Done()
		subc.Cancel()
	}()
	return subc
}

// Done returns a channel that is closed when the Context is cancelled
// or finished.
func (c *Context) Done() <-chan struct{} {
	if c == nil {
		return nil
	}
	return c.done
}

// IsCanceled reports whether this context has been canceled.
func (c *Context) IsCanceled() bool {
	select {
	case <-c.Done():
		return true
	default:
		return false
	}
}

// Cancel cancels the context. It is idempotent.
func (c *Context) Cancel() {
	if c == nil {
		return
	}
	c.cancelOnce.Do(c.cancel)
}

func (c *Context) cancel() {
	if c.done != nil {
		close(c.done)
	}
}
<|endoftext|>


<commit_before>package version

// Version is the version of the build.
const Version = "0.1.20"
<commit_msg>bump back to v0.1.21-dev<commit_after>package version

// Version is the version of the build.
const Version = "0.1.21-dev"
<|endoftext|>


<commit_before>package version

var (
	Version   = "3.0.2"
	GitCommit = "HEAD"
)
<commit_msg>bump 3.0.3<commit_after>package version

var (
	Version   = "3.0.3"
	GitCommit = "HEAD"
)
<|endoftext|>


<commit_before>package version

var (
	Version = "0.7.1"

	// git hash should be filled by:
	// 	go build -ldflags="-X github.com/cayleygraph/cayley/version.GitHash=xxxx"

	GitHash   = "dev snapshot"
	BuildDate string
)
<commit_msg>bump version to 0.7.2<commit_after>package version

var (
	Version = "0.7.2"

	// git hash should be filled by:
	// 	go build -ldflags="-X github.com/cayleygraph/cayley/version.GitHash=xxxx"

	GitHash   = "dev snapshot"
	BuildDate string
)
<|endoftext|>


<commit_before>package version

var (
	// VERSION should be updated by hand at each release
	VERSION = "0.2.0"

	// GITCOMMIT will be overwritten automatically by the build system
	GITCOMMIT = "HEAD"
)
<commit_msg>bump version to 0.3.0<commit_after>package version

var (
	// VERSION should be updated by hand at each release
	VERSION = "0.3.0"

	// GITCOMMIT will be overwritten automatically by the build system
	GITCOMMIT = "HEAD"
)
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package handler

import (
	"bytes"
	"compress/gzip"
	"crypto/sha512"
	"encoding/json"
	"fmt"
	"gopkg.in/yaml.v2"
	"mime"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/NYTimes/gziphandler"
	"github.com/emicklei/go-restful"
	"github.com/go-openapi/spec"
	"github.com/golang/protobuf/proto"
	"github.com/googleapis/gnostic/OpenAPIv2"
	"github.com/googleapis/gnostic/compiler"

	"k8s.io/kube-openapi/pkg/builder"
	"k8s.io/kube-openapi/pkg/common"
)

const (
	jsonExt = ".json"

	mimeJson = "application/json"
	// TODO(mehdy): change @68f4ded to a version tag when gnostic add version tags.
	mimePb   = "application/com.github.googleapis.gnostic.OpenAPIv2@68f4ded+protobuf"
	mimePbGz = "application/x-gzip"
)

// OpenAPIService is the service responsible for serving OpenAPI spec. It has
// the ability to safely change the spec while serving it.
type OpenAPIService struct {
	// rwMutex protects All members of this service.
	rwMutex sync.RWMutex

	orgSpec      *spec.Swagger
	lastModified time.Time

	specBytes []byte
	specPb    []byte
	specPbGz  []byte

	specBytesETag string
	specPbETag    string
	specPbGzETag  string
}

func init() {
	mime.AddExtensionType(".json", mimeJson)
	mime.AddExtensionType(".pb-v1", mimePb)
	mime.AddExtensionType(".gz", mimePbGz)
}

func computeETag(data []byte) string {
	return fmt.Sprintf("\"%X\"", sha512.Sum512(data))
}

// BuildAndRegisterOpenAPIService builds the spec and registers a handler to provides access to it.
// Use this method if your OpenAPI spec is static. If you want to update the spec, use BuildOpenAPISpec then RegisterOpenAPIService.
func BuildAndRegisterOpenAPIService(servePath string, webServices []*restful.WebService, config *common.Config, handler common.PathHandler) (*OpenAPIService, error) {
	spec, err := builder.BuildOpenAPISpec(webServices, config)
	if err != nil {
		return nil, err
	}
	return RegisterOpenAPIService(spec, servePath, handler)
}

// RegisterOpenAPIService registers a handler to provides access to provided swagger spec.
// Note: servePath should end with ".json" as the RegisterOpenAPIService assume it is serving a
// json file and will also serve .pb and .gz files.
func RegisterOpenAPIService(openapiSpec *spec.Swagger, servePath string, handler common.PathHandler) (*OpenAPIService, error) {
	if !strings.HasSuffix(servePath, jsonExt) {
		return nil, fmt.Errorf("serving path must end with \"%s\"", jsonExt)
	}

	servePathBase := strings.TrimSuffix(servePath, jsonExt)

	o := OpenAPIService{}
	if err := o.UpdateSpec(openapiSpec); err != nil {
		return nil, err
	}

	type fileInfo struct {
		ext            string
		getDataAndETag func() ([]byte, string, time.Time)
	}

	files := []fileInfo{
		{".json", o.getSwaggerBytes},
		{"-2.0.0.json", o.getSwaggerBytes},
		{"-2.0.0.pb-v1", o.getSwaggerPbBytes},
		{"-2.0.0.pb-v1.gz", o.getSwaggerPbGzBytes},
	}

	for _, file := range files {
		path := servePathBase + file.ext
		getDataAndETag := file.getDataAndETag
		handler.Handle(path, gziphandler.GzipHandler(http.HandlerFunc(
			func(w http.ResponseWriter, r *http.Request) {
				data, etag, lastModified := getDataAndETag()
				w.Header().Set("Etag", etag)

				// ServeContent will take care of caching using eTag.
				http.ServeContent(w, r, path, lastModified, bytes.NewReader(data))
			}),
		))
	}

	return &o, nil
}

func (o *OpenAPIService) getSwaggerBytes() ([]byte, string, time.Time) {
	o.rwMutex.RLock()
	defer o.rwMutex.RUnlock()
	return o.specBytes, o.specBytesETag, o.lastModified
}

func (o *OpenAPIService) getSwaggerPbBytes() ([]byte, string, time.Time) {
	o.rwMutex.RLock()
	defer o.rwMutex.RUnlock()
	return o.specPb, o.specPbETag, o.lastModified
}

func (o *OpenAPIService) getSwaggerPbGzBytes() ([]byte, string, time.Time) {
	o.rwMutex.RLock()
	defer o.rwMutex.RUnlock()
	return o.specPbGz, o.specPbGzETag, o.lastModified
}

func (o *OpenAPIService) UpdateSpec(openapiSpec *spec.Swagger) (err error) {
	orgSpec := openapiSpec
	specBytes, err := json.MarshalIndent(openapiSpec, " ", " ")
	if err != nil {
		return err
	}
	specPb, err := toProtoBinary(specBytes)
	if err != nil {
		return err
	}
	specPbGz := toGzip(specPb)

	specBytesETag := computeETag(specBytes)
	specPbETag := computeETag(specPb)
	specPbGzETag := computeETag(specPbGz)

	lastModified := time.Now()

	o.rwMutex.Lock()
	defer o.rwMutex.Unlock()

	o.orgSpec = orgSpec
	o.specBytes = specBytes
	o.specPb = specPb
	o.specPbGz = specPbGz
	o.specBytesETag = specBytesETag
	o.specPbETag = specPbETag
	o.specPbGzETag = specPbGzETag
	o.lastModified = lastModified

	return nil
}

func toProtoBinary(spec []byte) ([]byte, error) {
	var info yaml.MapSlice
	err := yaml.Unmarshal(spec, &info)
	if err != nil {
		return nil, err
	}
	document, err := openapi_v2.NewDocument(info, compiler.NewContext("$root", nil))
	if err != nil {
		return nil, err
	}
	return proto.Marshal(document)
}

func toGzip(data []byte) []byte {
	var buf bytes.Buffer
	zw := gzip.NewWriter(&buf)
	zw.Write(data)
	zw.Close()
	return buf.Bytes()
}
<commit_msg>Implement OpenAPI endpoint<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package handler

import (
	"bytes"
	"compress/gzip"
	"crypto/sha512"
	"encoding/json"
	"fmt"
	"mime"
	"net/http"
	"strings"
	"sync"
	"time"

	yaml "gopkg.in/yaml.v2"

	"github.com/NYTimes/gziphandler"
	restful "github.com/emicklei/go-restful"
	"github.com/go-openapi/spec"
	"github.com/golang/protobuf/proto"
	openapi_v2 "github.com/googleapis/gnostic/OpenAPIv2"
	"github.com/googleapis/gnostic/compiler"

	"k8s.io/kube-openapi/pkg/builder"
	"k8s.io/kube-openapi/pkg/common"
)

const (
	jsonExt = ".json"

	mimeJson = "application/json"
	// TODO(mehdy): change @68f4ded to a version tag when gnostic add version tags.
	mimePb   = "application/com.github.googleapis.gnostic.OpenAPIv2@68f4ded+protobuf"
	mimePbGz = "application/x-gzip"
)

// OpenAPIService is the service responsible for serving OpenAPI spec. It has
// the ability to safely change the spec while serving it.
type OpenAPIService struct {
	// rwMutex protects All members of this service.
	rwMutex sync.RWMutex

	orgSpec      *spec.Swagger
	lastModified time.Time

	specBytes []byte
	specPb    []byte
	specPbGz  []byte

	specBytesETag string
	specPbETag    string
	specPbGzETag  string
}

func init() {
	mime.AddExtensionType(".json", mimeJson)
	mime.AddExtensionType(".pb-v1", mimePb)
	mime.AddExtensionType(".gz", mimePbGz)
}

func computeETag(data []byte) string {
	return fmt.Sprintf("\"%X\"", sha512.Sum512(data))
}

// NOTE: [DEPRECATION] We will announce deprecation for format-separated endpoints for OpenAPI spec,
// and switch to a single /openapi/v2 endpoint in Kubernetes 1.9. The design doc and deprecation process
// are tracked at: https://docs.google.com/document/d/19lEqE9lc4yHJ3WJAJxS_G7TcORIJXGHyq3wpwcH28nU.
//
// BuildAndRegisterOpenAPIService builds the spec and registers a handler to provides access to it.
// Use this method if your OpenAPI spec is static. If you want to update the spec, use BuildOpenAPISpec then RegisterOpenAPIService.
func BuildAndRegisterOpenAPIService(servePath string, webServices []*restful.WebService, config *common.Config, handler common.PathHandler) (*OpenAPIService, error) {
	spec, err := builder.BuildOpenAPISpec(webServices, config)
	if err != nil {
		return nil, err
	}
	return RegisterOpenAPIService(spec, servePath, handler)
}

// NOTE: [DEPRECATION] We will announce deprecation for format-separated endpoints for OpenAPI spec,
// and switch to a single /openapi/v2 endpoint in Kubernetes 1.9. The design doc and deprecation process
// are tracked at: https://docs.google.com/document/d/19lEqE9lc4yHJ3WJAJxS_G7TcORIJXGHyq3wpwcH28nU.
//
// RegisterOpenAPIService registers a handler to provides access to provided swagger spec.
// Note: servePath should end with ".json" as the RegisterOpenAPIService assume it is serving a
// json file and will also serve .pb and .gz files.
func RegisterOpenAPIService(openapiSpec *spec.Swagger, servePath string, handler common.PathHandler) (*OpenAPIService, error) {
	if !strings.HasSuffix(servePath, jsonExt) {
		return nil, fmt.Errorf("serving path must end with \"%s\"", jsonExt)
	}

	servePathBase := strings.TrimSuffix(servePath, jsonExt)

	o := OpenAPIService{}
	if err := o.UpdateSpec(openapiSpec); err != nil {
		return nil, err
	}

	type fileInfo struct {
		ext            string
		getDataAndETag func() ([]byte, string, time.Time)
	}

	files := []fileInfo{
		{".json", o.getSwaggerBytes},
		{"-2.0.0.json", o.getSwaggerBytes},
		{"-2.0.0.pb-v1", o.getSwaggerPbBytes},
		{"-2.0.0.pb-v1.gz", o.getSwaggerPbGzBytes},
	}

	for _, file := range files {
		path := servePathBase + file.ext
		getDataAndETag := file.getDataAndETag
		handler.Handle(path, gziphandler.GzipHandler(http.HandlerFunc(
			func(w http.ResponseWriter, r *http.Request) {
				data, etag, lastModified := getDataAndETag()
				w.Header().Set("Etag", etag)

				// ServeContent will take care of caching using eTag.
				http.ServeContent(w, r, path, lastModified, bytes.NewReader(data))
			}),
		))
	}

	return &o, nil
}

func (o *OpenAPIService) getSwaggerBytes() ([]byte, string, time.Time) {
	o.rwMutex.RLock()
	defer o.rwMutex.RUnlock()
	return o.specBytes, o.specBytesETag, o.lastModified
}

func (o *OpenAPIService) getSwaggerPbBytes() ([]byte, string, time.Time) {
	o.rwMutex.RLock()
	defer o.rwMutex.RUnlock()
	return o.specPb, o.specPbETag, o.lastModified
}

func (o *OpenAPIService) getSwaggerPbGzBytes() ([]byte, string, time.Time) {
	o.rwMutex.RLock()
	defer o.rwMutex.RUnlock()
	return o.specPbGz, o.specPbGzETag, o.lastModified
}

func (o *OpenAPIService) UpdateSpec(openapiSpec *spec.Swagger) (err error) {
	orgSpec := openapiSpec
	specBytes, err := json.MarshalIndent(openapiSpec, " ", " ")
	if err != nil {
		return err
	}
	specPb, err := toProtoBinary(specBytes)
	if err != nil {
		return err
	}
	specPbGz := toGzip(specPb)

	specBytesETag := computeETag(specBytes)
	specPbETag := computeETag(specPb)
	specPbGzETag := computeETag(specPbGz)

	lastModified := time.Now()

	o.rwMutex.Lock()
	defer o.rwMutex.Unlock()

	o.orgSpec = orgSpec
	o.specBytes = specBytes
	o.specPb = specPb
	o.specPbGz = specPbGz
	o.specBytesETag = specBytesETag
	o.specPbETag = specPbETag
	o.specPbGzETag = specPbGzETag
	o.lastModified = lastModified

	return nil
}

func toProtoBinary(spec []byte) ([]byte, error) {
	var info yaml.MapSlice
	err := yaml.Unmarshal(spec, &info)
	if err != nil {
		return nil, err
	}
	document, err := openapi_v2.NewDocument(info, compiler.NewContext("$root", nil))
	if err != nil {
		return nil, err
	}
	return proto.Marshal(document)
}

func toGzip(data []byte) []byte {
	var buf bytes.Buffer
	zw := gzip.NewWriter(&buf)
	zw.Write(data)
	zw.Close()
	return buf.Bytes()
}

// RegisterOpenAPIVersionedService registers a handler to provides access to provided swagger spec.
func RegisterOpenAPIVersionedService(openapiSpec *spec.Swagger, servePath string, handler common.PathHandler) (*OpenAPIService, error) {
	o := OpenAPIService{}
	if err := o.UpdateSpec(openapiSpec); err != nil {
		return nil, err
	}

	files := map[string]func() ([]byte, string, time.Time){
		"application/json":                                           o.getSwaggerBytes,
		"application/com.github.proto-openapi.spec.v2@v1.0+protobuf": o.getSwaggerPbBytes,
	}

	handler.Handle(servePath, gziphandler.GzipHandler(http.HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			var getDataAndETag func() ([]byte, string, time.Time)
			// Get the first value associated with header "Accept"
			decipherableFormat := r.Header.Get("Accept")
			if decipherableFormat == "" || decipherableFormat == "*/*" {
				// If "Accept" header is not set or "*/*", serve json file as default
				decipherableFormat = "application/json"
			}
			w.Header().Add("Vary", "Accept")

			getDataAndETag = files[decipherableFormat]
			if getDataAndETag == nil {
				// Return 406 for not acceptable format
				w.WriteHeader(406)
				return
			}
			data, etag, lastModified := getDataAndETag()
			w.Header().Set("Etag", etag)

			// ServeContent will take care of caching using eTag.
			http.ServeContent(w, r, servePath, lastModified, bytes.NewReader(data))
		}),
	))

	return &o, nil
}

// BuildAndRegisterOpenAPIVersionedService builds the spec and registers a handler to provides access to it.
// Use this method if your OpenAPI spec is static. If you want to update the spec, use BuildOpenAPISpec then RegisterOpenAPIVersionedService.
func BuildAndRegisterOpenAPIVersionedService(servePath string, webServices []*restful.WebService, config *common.Config, handler common.PathHandler) (*OpenAPIService, error) {
	spec, err := builder.BuildOpenAPISpec(webServices, config)
	if err != nil {
		return nil, err
	}
	return RegisterOpenAPIVersionedService(spec, servePath, handler)
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package handler

import (
	"bytes"
	"compress/gzip"
	"crypto/sha512"
	"fmt"
	"mime"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/NYTimes/gziphandler"
	restful "github.com/emicklei/go-restful"
	"github.com/go-openapi/spec"
	"github.com/golang/protobuf/proto"
	openapi_v2 "github.com/googleapis/gnostic/OpenAPIv2"
	"github.com/googleapis/gnostic/compiler"
	"github.com/json-iterator/go"
	"github.com/munnerz/goautoneg"
	yaml "gopkg.in/yaml.v2"

	"k8s.io/kube-openapi/pkg/builder"
	"k8s.io/kube-openapi/pkg/common"
)

const (
	jsonExt = ".json"

	mimeJson = "application/json"
	// TODO(mehdy): change @68f4ded to a version tag when gnostic add version tags.
	mimePb   = "application/com.github.googleapis.gnostic.OpenAPIv2@68f4ded+protobuf"
	mimePbGz = "application/x-gzip"
)

// OpenAPIService is the service responsible for serving OpenAPI spec. It has
// the ability to safely change the spec while serving it.
type OpenAPIService struct {
	// rwMutex protects All members of this service.
	rwMutex sync.RWMutex

	lastModified time.Time

	specBytes []byte
	specPb    []byte
	specPbGz  []byte

	specBytesETag string
	specPbETag    string
	specPbGzETag  string
}

func init() {
	mime.AddExtensionType(".json", mimeJson)
	mime.AddExtensionType(".pb-v1", mimePb)
	mime.AddExtensionType(".gz", mimePbGz)
}

func computeETag(data []byte) string {
	return fmt.Sprintf("\"%X\"", sha512.Sum512(data))
}

// NewOpenAPIService builds an OpenAPIService starting with the given spec.
func NewOpenAPIService(spec *spec.Swagger) (*OpenAPIService, error) {
	o := &OpenAPIService{}
	if err := o.UpdateSpec(spec); err != nil {
		return nil, err
	}
	return o, nil
}

// NOTE: [DEPRECATION] We will announce deprecation for format-separated endpoints for OpenAPI spec,
// and switch to a single /openapi/v2 endpoint in Kubernetes 1.10. The design doc and deprecation process
// are tracked at: https://docs.google.com/document/d/19lEqE9lc4yHJ3WJAJxS_G7TcORIJXGHyq3wpwcH28nU.
//
// BuildAndRegisterOpenAPIService builds the spec and registers a handler to provide access to it.
// Use this method if your OpenAPI spec is static. If you want to update the spec, use BuildOpenAPISpec then RegisterOpenAPIService.
func BuildAndRegisterOpenAPIService(servePath string, webServices []*restful.WebService, config *common.Config, handler common.PathHandler) (*OpenAPIService, error) {
	spec, err := builder.BuildOpenAPISpec(webServices, config)
	if err != nil {
		return nil, err
	}
	o, err := NewOpenAPIService(spec)
	if err != nil {
		return nil, err
	}
	return o, o.RegisterOpenAPIService(servePath, handler)
}

// NOTE: [DEPRECATION] We will announce deprecation for format-separated endpoints for OpenAPI spec,
// and switch to a single /openapi/v2 endpoint in Kubernetes 1.10. The design doc and deprecation process
// are tracked at: https://docs.google.com/document/d/19lEqE9lc4yHJ3WJAJxS_G7TcORIJXGHyq3wpwcH28nU.
//
// RegisterOpenAPIService registers a handler to provide access to provided swagger spec.
// Note: servePath should end with ".json" as the RegisterOpenAPIService assume it is serving a
// json file and will also serve .pb and .gz files.
//
// Deprecated: use OpenAPIService.RegisterOpenAPIService instead.
func RegisterOpenAPIService(spec *spec.Swagger, servePath string, handler common.PathHandler) (*OpenAPIService, error) {
	o, err := NewOpenAPIService(spec)
	if err != nil {
		return nil, err
	}
	return o, o.RegisterOpenAPIService(servePath, handler)
}

// NOTE: [DEPRECATION] We will announce deprecation for format-separated endpoints for OpenAPI spec,
// and switch to a single /openapi/v2 endpoint in Kubernetes 1.10. The design doc and deprecation process
// are tracked at: https://docs.google.com/document/d/19lEqE9lc4yHJ3WJAJxS_G7TcORIJXGHyq3wpwcH28nU.
//
// RegisterOpenAPIService registers a handler to provide access to provided swagger spec.
// Note: servePath should end with ".json" as the RegisterOpenAPIService assume it is serving a
// json file and will also serve .pb and .gz files.
func (o *OpenAPIService) RegisterOpenAPIService(servePath string, handler common.PathHandler) error {
	if !strings.HasSuffix(servePath, jsonExt) {
		return fmt.Errorf("serving path must end with \"%s\"", jsonExt)
	}

	servePathBase := strings.TrimSuffix(servePath, jsonExt)

	type fileInfo struct {
		ext            string
		getDataAndETag func() ([]byte, string, time.Time)
	}

	files := []fileInfo{
		{".json", o.getSwaggerBytes},
		{"-2.0.0.json", o.getSwaggerBytes},
		{"-2.0.0.pb-v1", o.getSwaggerPbBytes},
		{"-2.0.0.pb-v1.gz", o.getSwaggerPbGzBytes},
	}

	for _, file := range files {
		path := servePathBase + file.ext
		getDataAndETag := file.getDataAndETag
		handler.Handle(path, gziphandler.GzipHandler(http.HandlerFunc(
			func(w http.ResponseWriter, r *http.Request) {
				data, etag, lastModified := getDataAndETag()
				w.Header().Set("Etag", etag)

				// ServeContent will take care of caching using eTag.
				http.ServeContent(w, r, path, lastModified, bytes.NewReader(data))
			}),
		))
	}

	return nil
}

func (o *OpenAPIService) getSwaggerBytes() ([]byte, string, time.Time) {
	o.rwMutex.RLock()
	defer o.rwMutex.RUnlock()
	return o.specBytes, o.specBytesETag, o.lastModified
}

func (o *OpenAPIService) getSwaggerPbBytes() ([]byte, string, time.Time) {
	o.rwMutex.RLock()
	defer o.rwMutex.RUnlock()
	return o.specPb, o.specPbETag, o.lastModified
}

func (o *OpenAPIService) getSwaggerPbGzBytes() ([]byte, string, time.Time) {
	o.rwMutex.RLock()
	defer o.rwMutex.RUnlock()
	return o.specPbGz, o.specPbGzETag, o.lastModified
}

func (o *OpenAPIService) UpdateSpec(openapiSpec *spec.Swagger) (err error) {
	specBytes, err := jsoniter.Marshal(openapiSpec)
	if err != nil {
		return err
	}
	var json map[string]interface{}
	if err := jsoniter.Unmarshal(specBytes, &json); err != nil {
		return err
	}
	specPb, err := ToProtoBinary(json)
	if err != nil {
		return err
	}
	specPbGz := toGzip(specPb)

	specBytesETag := computeETag(specBytes)
	specPbETag := computeETag(specPb)
	specPbGzETag := computeETag(specPbGz)

	lastModified := time.Now()

	o.rwMutex.Lock()
	defer o.rwMutex.Unlock()

	o.specBytes = specBytes
	o.specPb = specPb
	o.specPbGz = specPbGz
	o.specBytesETag = specBytesETag
	o.specPbETag = specPbETag
	o.specPbGzETag = specPbGzETag
	o.lastModified = lastModified

	return nil
}

func jsonToYAML(j map[string]interface{}) yaml.MapSlice {
	if j == nil {
		return nil
	}
	ret := make(yaml.MapSlice, 0, len(j))
	for k, v := range j {
		ret = append(ret, yaml.MapItem{k, jsonToYAMLValue(v)})
	}
	return ret
}

func jsonToYAMLValue(j interface{}) interface{} {
	switch j := j.(type) {
	case map[string]interface{}:
		return jsonToYAML(j)
	case []interface{}:
		ret := make([]interface{}, len(j))
		for i := range j {
			ret[i] = jsonToYAMLValue(j[i])
		}
		return ret
	}
	return j
}

func ToProtoBinary(json map[string]interface{}) ([]byte, error) {
	document, err := openapi_v2.NewDocument(jsonToYAML(json), compiler.NewContext("$root", nil))
	if err != nil {
		return nil, err
	}
	return proto.Marshal(document)
}

func toGzip(data []byte) []byte {
	var buf bytes.Buffer
	zw := gzip.NewWriter(&buf)
	zw.Write(data)
	zw.Close()
	return buf.Bytes()
}

// RegisterOpenAPIVersionedService registers a handler to provide access to provided swagger spec.
//
// Deprecated: use OpenAPIService.RegisterOpenAPIVersionedService instead.
func RegisterOpenAPIVersionedService(spec *spec.Swagger, servePath string, handler common.PathHandler) (*OpenAPIService, error) {
	o, err := NewOpenAPIService(spec)
	if err != nil {
		return nil, err
	}
	return o, o.RegisterOpenAPIVersionedService(servePath, handler)
}

// RegisterOpenAPIVersionedService registers a handler to provide access to provided swagger spec.
func (o *OpenAPIService) RegisterOpenAPIVersionedService(servePath string, handler common.PathHandler) error {
	accepted := []struct {
		Type           string
		SubType        string
		GetDataAndETag func() ([]byte, string, time.Time)
	}{
		{"application", "json", o.getSwaggerBytes},
		{"application", "com.github.proto-openapi.spec.v2@v1.0+protobuf", o.getSwaggerPbBytes},
	}

	handler.Handle(servePath, gziphandler.GzipHandler(http.HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			decipherableFormats := r.Header.Get("Accept")
			if decipherableFormats == "" {
				decipherableFormats = "*/*"
			}
			clauses := goautoneg.ParseAccept(decipherableFormats)
			w.Header().Add("Vary", "Accept")
			for _, clause := range clauses {
				for _, accepts := range accepted {
					if clause.Type != accepts.Type && clause.Type != "*" {
						continue
					}
					if clause.SubType != accepts.SubType && clause.SubType != "*" {
						continue
					}

					// serve the first matching media type in the sorted clause list
					data, etag, lastModified := accepts.GetDataAndETag()
					w.Header().Set("Etag", etag)
					// ServeContent will take care of caching using eTag.
					http.ServeContent(w, r, servePath, lastModified, bytes.NewReader(data))
					return
				}
			}
			// Return 406 for not acceptable format
			w.WriteHeader(406)
			return
		}),
	))

	return nil
}

// BuildAndRegisterOpenAPIVersionedService builds the spec and registers a handler to provide access to it.
// Use this method if your OpenAPI spec is static. If you want to update the spec, use BuildOpenAPISpec then RegisterOpenAPIVersionedService.
func BuildAndRegisterOpenAPIVersionedService(servePath string, webServices []*restful.WebService, config *common.Config, handler common.PathHandler) (*OpenAPIService, error) {
	spec, err := builder.BuildOpenAPISpec(webServices, config)
	if err != nil {
		return nil, err
	}
	o, err := NewOpenAPIService(spec)
	if err != nil {
		return nil, err
	}
	return o, o.RegisterOpenAPIVersionedService(servePath, handler)
}
<commit_msg>handler: use stdlib compatible json-iter encoder for stable map order<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package handler

import (
	"bytes"
	"compress/gzip"
	"crypto/sha512"
	"fmt"
	"mime"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/NYTimes/gziphandler"
	restful "github.com/emicklei/go-restful"
	"github.com/go-openapi/spec"
	"github.com/golang/protobuf/proto"
	openapi_v2 "github.com/googleapis/gnostic/OpenAPIv2"
	"github.com/googleapis/gnostic/compiler"
	"github.com/json-iterator/go"
	"github.com/munnerz/goautoneg"
	yaml "gopkg.in/yaml.v2"

	"k8s.io/kube-openapi/pkg/builder"
	"k8s.io/kube-openapi/pkg/common"
)

const (
	jsonExt = ".json"

	mimeJson = "application/json"
	// TODO(mehdy): change @68f4ded to a version tag when gnostic add version tags.
	mimePb   = "application/com.github.googleapis.gnostic.OpenAPIv2@68f4ded+protobuf"
	mimePbGz = "application/x-gzip"
)

// OpenAPIService is the service responsible for serving OpenAPI spec. It has
// the ability to safely change the spec while serving it.
type OpenAPIService struct {
	// rwMutex protects All members of this service.
	rwMutex sync.RWMutex

	lastModified time.Time

	specBytes []byte
	specPb    []byte
	specPbGz  []byte

	specBytesETag string
	specPbETag    string
	specPbGzETag  string
}

func init() {
	mime.AddExtensionType(".json", mimeJson)
	mime.AddExtensionType(".pb-v1", mimePb)
	mime.AddExtensionType(".gz", mimePbGz)
}

func computeETag(data []byte) string {
	return fmt.Sprintf("\"%X\"", sha512.Sum512(data))
}

// NewOpenAPIService builds an OpenAPIService starting with the given spec.
func NewOpenAPIService(spec *spec.Swagger) (*OpenAPIService, error) {
	o := &OpenAPIService{}
	if err := o.UpdateSpec(spec); err != nil {
		return nil, err
	}
	return o, nil
}

// NOTE: [DEPRECATION] We will announce deprecation for format-separated endpoints for OpenAPI spec,
// and switch to a single /openapi/v2 endpoint in Kubernetes 1.10. The design doc and deprecation process
// are tracked at: https://docs.google.com/document/d/19lEqE9lc4yHJ3WJAJxS_G7TcORIJXGHyq3wpwcH28nU.
//
// BuildAndRegisterOpenAPIService builds the spec and registers a handler to provide access to it.
// Use this method if your OpenAPI spec is static. If you want to update the spec, use BuildOpenAPISpec then RegisterOpenAPIService.
func BuildAndRegisterOpenAPIService(servePath string, webServices []*restful.WebService, config *common.Config, handler common.PathHandler) (*OpenAPIService, error) {
	spec, err := builder.BuildOpenAPISpec(webServices, config)
	if err != nil {
		return nil, err
	}
	o, err := NewOpenAPIService(spec)
	if err != nil {
		return nil, err
	}
	return o, o.RegisterOpenAPIService(servePath, handler)
}

// NOTE: [DEPRECATION] We will announce deprecation for format-separated endpoints for OpenAPI spec,
// and switch to a single /openapi/v2 endpoint in Kubernetes 1.10. The design doc and deprecation process
// are tracked at: https://docs.google.com/document/d/19lEqE9lc4yHJ3WJAJxS_G7TcORIJXGHyq3wpwcH28nU.
//
// RegisterOpenAPIService registers a handler to provide access to provided swagger spec.
// Note: servePath should end with ".json" as the RegisterOpenAPIService assume it is serving a
// json file and will also serve .pb and .gz files.
//
// Deprecated: use OpenAPIService.RegisterOpenAPIService instead.
func RegisterOpenAPIService(spec *spec.Swagger, servePath string, handler common.PathHandler) (*OpenAPIService, error) {
	o, err := NewOpenAPIService(spec)
	if err != nil {
		return nil, err
	}
	return o, o.RegisterOpenAPIService(servePath, handler)
}

// NOTE: [DEPRECATION] We will announce deprecation for format-separated endpoints for OpenAPI spec,
// and switch to a single /openapi/v2 endpoint in Kubernetes 1.10. The design doc and deprecation process
// are tracked at: https://docs.google.com/document/d/19lEqE9lc4yHJ3WJAJxS_G7TcORIJXGHyq3wpwcH28nU.
//
// RegisterOpenAPIService registers a handler to provide access to provided swagger spec.
// Note: servePath should end with ".json" as the RegisterOpenAPIService assume it is serving a
// json file and will also serve .pb and .gz files.
func (o *OpenAPIService) RegisterOpenAPIService(servePath string, handler common.PathHandler) error {
	if !strings.HasSuffix(servePath, jsonExt) {
		return fmt.Errorf("serving path must end with \"%s\"", jsonExt)
	}

	servePathBase := strings.TrimSuffix(servePath, jsonExt)

	type fileInfo struct {
		ext            string
		getDataAndETag func() ([]byte, string, time.Time)
	}

	files := []fileInfo{
		{".json", o.getSwaggerBytes},
		{"-2.0.0.json", o.getSwaggerBytes},
		{"-2.0.0.pb-v1", o.getSwaggerPbBytes},
		{"-2.0.0.pb-v1.gz", o.getSwaggerPbGzBytes},
	}

	for _, file := range files {
		path := servePathBase + file.ext
		getDataAndETag := file.getDataAndETag
		handler.Handle(path, gziphandler.GzipHandler(http.HandlerFunc(
			func(w http.ResponseWriter, r *http.Request) {
				data, etag, lastModified := getDataAndETag()
				w.Header().Set("Etag", etag)

				// ServeContent will take care of caching using eTag.
				http.ServeContent(w, r, path, lastModified, bytes.NewReader(data))
			}),
		))
	}

	return nil
}

func (o *OpenAPIService) getSwaggerBytes() ([]byte, string, time.Time) {
	o.rwMutex.RLock()
	defer o.rwMutex.RUnlock()
	return o.specBytes, o.specBytesETag, o.lastModified
}

func (o *OpenAPIService) getSwaggerPbBytes() ([]byte, string, time.Time) {
	o.rwMutex.RLock()
	defer o.rwMutex.RUnlock()
	return o.specPb, o.specPbETag, o.lastModified
}

func (o *OpenAPIService) getSwaggerPbGzBytes() ([]byte, string, time.Time) {
	o.rwMutex.RLock()
	defer o.rwMutex.RUnlock()
	return o.specPbGz, o.specPbGzETag, o.lastModified
}

func (o *OpenAPIService) UpdateSpec(openapiSpec *spec.Swagger) (err error) {
	specBytes, err := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(openapiSpec)
	if err != nil {
		return err
	}
	var json map[string]interface{}
	if err := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(specBytes, &json); err != nil {
		return err
	}
	specPb, err := ToProtoBinary(json)
	if err != nil {
		return err
	}
	specPbGz := toGzip(specPb)

	specBytesETag := computeETag(specBytes)
	specPbETag := computeETag(specPb)
	specPbGzETag := computeETag(specPbGz)

	lastModified := time.Now()

	o.rwMutex.Lock()
	defer o.rwMutex.Unlock()

	o.specBytes = specBytes
	o.specPb = specPb
	o.specPbGz = specPbGz
	o.specBytesETag = specBytesETag
	o.specPbETag = specPbETag
	o.specPbGzETag = specPbGzETag
	o.lastModified = lastModified

	return nil
}

func jsonToYAML(j map[string]interface{}) yaml.MapSlice {
	if j == nil {
		return nil
	}
	ret := make(yaml.MapSlice, 0, len(j))
	for k, v := range j {
		ret = append(ret, yaml.MapItem{k, jsonToYAMLValue(v)})
	}
	return ret
}

func jsonToYAMLValue(j interface{}) interface{} {
	switch j := j.(type) {
	case map[string]interface{}:
		return jsonToYAML(j)
	case []interface{}:
		ret := make([]interface{}, len(j))
		for i := range j {
			ret[i] = jsonToYAMLValue(j[i])
		}
		return ret
	}
	return j
}

func ToProtoBinary(json map[string]interface{}) ([]byte, error) {
	document, err := openapi_v2.NewDocument(jsonToYAML(json), compiler.NewContext("$root", nil))
	if err != nil {
		return nil, err
	}
	return proto.Marshal(document)
}

func toGzip(data []byte) []byte {
	var buf bytes.Buffer
	zw := gzip.NewWriter(&buf)
	zw.Write(data)
	zw.Close()
	return buf.Bytes()
}

// RegisterOpenAPIVersionedService registers a handler to provide access to provided swagger spec.
//
// Deprecated: use OpenAPIService.RegisterOpenAPIVersionedService instead.
func RegisterOpenAPIVersionedService(spec *spec.Swagger, servePath string, handler common.PathHandler) (*OpenAPIService, error) {
	o, err := NewOpenAPIService(spec)
	if err != nil {
		return nil, err
	}
	return o, o.RegisterOpenAPIVersionedService(servePath, handler)
}

// RegisterOpenAPIVersionedService registers a handler to provide access to provided swagger spec.
func (o *OpenAPIService) RegisterOpenAPIVersionedService(servePath string, handler common.PathHandler) error {
	accepted := []struct {
		Type           string
		SubType        string
		GetDataAndETag func() ([]byte, string, time.Time)
	}{
		{"application", "json", o.getSwaggerBytes},
		{"application", "com.github.proto-openapi.spec.v2@v1.0+protobuf", o.getSwaggerPbBytes},
	}

	handler.Handle(servePath, gziphandler.GzipHandler(http.HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			decipherableFormats := r.Header.Get("Accept")
			if decipherableFormats == "" {
				decipherableFormats = "*/*"
			}
			clauses := goautoneg.ParseAccept(decipherableFormats)
			w.Header().Add("Vary", "Accept")
			for _, clause := range clauses {
				for _, accepts := range accepted {
					if clause.Type != accepts.Type && clause.Type != "*" {
						continue
					}
					if clause.SubType != accepts.SubType && clause.SubType != "*" {
						continue
					}

					// serve the first matching media type in the sorted clause list
					data, etag, lastModified := accepts.GetDataAndETag()
					w.Header().Set("Etag", etag)
					// ServeContent will take care of caching using eTag.
					http.ServeContent(w, r, servePath, lastModified, bytes.NewReader(data))
					return
				}
			}
			// Return 406 for not acceptable format
			w.WriteHeader(406)
			return
		}),
	))

	return nil
}

// BuildAndRegisterOpenAPIVersionedService builds the spec and registers a handler to provide access to it.
// Use this method if your OpenAPI spec is static. If you want to update the spec, use BuildOpenAPISpec then RegisterOpenAPIVersionedService.
func BuildAndRegisterOpenAPIVersionedService(servePath string, webServices []*restful.WebService, config *common.Config, handler common.PathHandler) (*OpenAPIService, error) {
	spec, err := builder.BuildOpenAPISpec(webServices, config)
	if err != nil {
		return nil, err
	}
	o, err := NewOpenAPIService(spec)
	if err != nil {
		return nil, err
	}
	return o, o.RegisterOpenAPIVersionedService(servePath, handler)
}
<|endoftext|>


<commit_before>// Copyright 2018 Authors of Cilium
//
// 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 ipcache

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

	"github.com/cilium/cilium/pkg/identity"
	"github.com/cilium/cilium/pkg/kvstore"
	"github.com/cilium/cilium/pkg/lock"
	"github.com/cilium/cilium/pkg/logging/logfields"

	"github.com/sirupsen/logrus"
)

const (
	// DefaultAddressSpace is the address space used if none is provided.
	// TODO - once pkg/node adds this to clusterConfiguration, remove.
	DefaultAddressSpace = "default"
)

var (
	// IPIdentitiesPath is the path to where endpoint IPs are stored in the key-value
	//store.
	IPIdentitiesPath = path.Join(kvstore.BaseKeyPrefix, "state", "ip", "v1")

	// AddressSpace is the address space (cluster, etc.) in which policy is
	// computed. It is determined by the orchestration system / runtime.
	AddressSpace = DefaultAddressSpace

	// globalMap wraps the kvstore and provides reference-tracking for keys
	// that are upserted or released from the kvstore.
	globalMap *kvReferenceCounter

	setupIPIdentityWatcher sync.Once
)

// store is a key-value store for an underlying implementation, provided to
// mock out the kvstore for unit testing.
type store interface {
	// update will insert the {key, value} tuple into the underlying
	// kvstore.
	upsert(key string, value []byte, lease bool) error

	// delete will remove the key from the underlying kvstore.
	release(key string) error
}

// kvstoreImplementation is a store implementation backed by the kvstore.
type kvstoreImplementation struct{}

// upsert places the mapping of {key, value} into the kvstore, optionally with
// a lease.
func (k kvstoreImplementation) upsert(key string, value []byte, lease bool) error {
	return kvstore.Update(key, value, lease)
}

// release removes the specified key from the kvstore.
func (k kvstoreImplementation) release(key string) error {
	return kvstore.Delete(key)
}

// kvReferenceCounter provides a thin wrapper around the kvstore which adds
// reference tracking for all entries being updated. When the first key is
// updated, it adds a reference to the kvstore and tracks the reference
// internally. Subsequent updates also update the kvstore, and add a reference.
// Deletes from the kvReferenceCounter are only propagated to the kvstore when
// the final reference is released.
//
// This has some small overlap with the pkg/kvstore/allocator but this is only
// a map from key to reference count rather than also tracking values.
type kvReferenceCounter struct {
	lock.Mutex
	store

	// keys is a map from key to reference count for locally-referenced
	// keys in the global kvstore.
	keys map[string]uint64

	// marshaledIPIDPair is map indexed by the key that contains the
	// marshaled IPIdentityPair
	marshaledIPIDPairs map[string][]byte
}

// newKVReferenceCounter creates a new reference counter using the specified
// store as the underlying location for key/value pairs to be stored.
func newKVReferenceCounter(s store) *kvReferenceCounter {
	return &kvReferenceCounter{
		store:              s,
		keys:               map[string]uint64{},
		marshaledIPIDPairs: map[string][]byte{},
	}
}

// upsert attempts to insert the specified {key, ipIDPair} into the kvstore. If
// the key has previously been upserted, increments a reference on the key.
// Always updates the underlying store with this {key, ipIDPair} tuple.
// Only adds a reference to the key if the upsert is successful.
func (r *kvReferenceCounter) upsert(ipKey string, ipIDPair identity.IPIdentityPair) error {
	marshaledIPIDPair, err := json.Marshal(ipIDPair)
	if err != nil {
		return err
	}

	log.WithFields(logrus.Fields{
		logfields.IPAddr:       ipIDPair.IP,
		logfields.IPMask:       ipIDPair.Mask,
		logfields.Identity:     ipIDPair.ID,
		logfields.Modification: Upsert,
	}).Debug("upserting CIDR->ID mapping to kvstore")

	r.Lock()
	defer r.Unlock()
	refcnt := r.keys[ipKey] // 0 if not found
	refcnt++
	err = r.store.upsert(ipKey, marshaledIPIDPair, true)
	if err == nil {
		r.keys[ipKey] = refcnt
		r.marshaledIPIDPairs[ipKey] = marshaledIPIDPair
	}
	return err
}

// release removes a reference to the specified key. If the number of
// references reaches 0, the key is removed from the underlying kvstore.
func (r *kvReferenceCounter) release(key string) (err error) {
	r.Lock()
	defer r.Unlock()

	refcnt, ok := r.keys[key] // 0 if not found
	// avoid underflow and report bug
	if !ok || refcnt == 0 {
		log.WithField("key", key).Error("BUG: attempt to release ipcache entry while refcnt == 0")
		return nil
	}

	refcnt--
	if refcnt == 0 {
		delete(r.keys, key)
		delete(r.marshaledIPIDPairs, key)
		err = r.store.release(key)
	} else {
		r.keys[key] = refcnt
	}
	return err
}

// UpsertIPToKVStore updates / inserts the provided IP->Identity mapping into the
// kvstore, which will subsequently trigger an event in NewIPIdentityWatcher().
func UpsertIPToKVStore(IP, hostIP net.IP, ID identity.NumericIdentity, metadata string) error {
	ipKey := path.Join(IPIdentitiesPath, AddressSpace, IP.String())
	ipIDPair := identity.IPIdentityPair{
		IP:       IP,
		ID:       ID,
		Metadata: metadata,
		HostIP:   hostIP,
	}

	marshaledIPIDPair, err := json.Marshal(ipIDPair)
	if err != nil {
		return err
	}

	log.WithFields(logrus.Fields{
		logfields.IPAddr:       ipIDPair.IP,
		logfields.Identity:     ipIDPair.ID,
		logfields.Modification: Upsert,
	}).Debug("upserting IP->ID mapping to kvstore")

	return globalMap.store.upsert(ipKey, marshaledIPIDPair, true)
}

// keyToIPNet returns the IPNet describing the key, whether it is a host, and
// an error (if one occurs)
func keyToIPNet(key string) (parsedPrefix *net.IPNet, host bool, err error) {
	requiredPrefix := fmt.Sprintf("%s/", path.Join(IPIdentitiesPath, AddressSpace))
	if !strings.HasPrefix(key, requiredPrefix) {
		err = fmt.Errorf("Found invalid key %s outside of prefix %s", key, IPIdentitiesPath)
		return
	}

	suffix := strings.TrimPrefix(key, requiredPrefix)

	// Key is formatted as "prefix/192.0.2.0/24" for CIDRs
	_, parsedPrefix, err = net.ParseCIDR(suffix)
	if err != nil {
		// Key is likely a host in the format "prefix/192.0.2.3"
		parsedIP := net.ParseIP(suffix)
		if parsedIP == nil {
			err = fmt.Errorf("unable to parse IP from suffix %s", suffix)
			return
		}
		err = nil
		host = true
		ipv4 := parsedIP.To4()
		bits := net.IPv6len * 8
		if ipv4 != nil {
			parsedIP = ipv4
			bits = net.IPv4len * 8
		}
		parsedPrefix = &net.IPNet{IP: parsedIP, Mask: net.CIDRMask(bits, bits)}
	}

	return
}

// DeleteIPFromKVStore removes the IP->Identity mapping for the specified ip
// from the kvstore, which will subsequently trigger an event in
// NewIPIdentityWatcher().
func DeleteIPFromKVStore(ip string) error {
	ipKey := path.Join(IPIdentitiesPath, AddressSpace, ip)
	return globalMap.store.release(ipKey)
}

// IPIdentityWatcher is a watcher that will notify when IP<->identity mappings
// change in the kvstore
type IPIdentityWatcher struct {
	backend  kvstore.BackendOperations
	stop     chan struct{}
	synced   chan struct{}
	stopOnce sync.Once
}

// NewIPIdentityWatcher creates a new IPIdentityWatcher using the specified
// kvstore backend
func NewIPIdentityWatcher(backend kvstore.BackendOperations) *IPIdentityWatcher {
	watcher := &IPIdentityWatcher{
		backend: backend,
		stop:    make(chan struct{}),
		synced:  make(chan struct{}),
	}

	return watcher
}

// Watch starts the watcher and blocks waiting for events. When events are
// received from the kvstore, All IPIdentityMappingListener are notified. The
// function returns when IPIdentityWatcher.Close() is called. The watcher will
// automatically restart as required.
func (iw *IPIdentityWatcher) Watch() {
restart:
	watcher := iw.backend.ListAndWatch("endpointIPWatcher", IPIdentitiesPath, 512)

	for {
		select {
		// Get events from channel as they come in.
		case event, ok := <-watcher.Events:
			if !ok {
				log.Debugf("%s closed, restarting watch", watcher.String())
				time.Sleep(500 * time.Millisecond)
				goto restart
			}

			scopedLog := log.WithFields(logrus.Fields{"kvstore-event": event.Typ.String(), "key": event.Key})
			scopedLog.Debug("Received event")

			// Synchronize local caching of endpoint IP to ipIDPair mapping with
			// operation key-value store has informed us about.
			//
			// To resolve conflicts between hosts and full CIDR prefixes:
			// - Insert hosts into the cache as ".../w.x.y.z"
			// - Insert CIDRS into the cache as ".../w.x.y.z/N"
			// - If a host entry created, notify the listeners.
			// - If a CIDR is created and there's no overlapping host
			//   entry, ie it is a less than fully masked CIDR, OR
			//   it is a fully masked CIDR and there is no corresponding
			//   host entry, then:
			//   - Notify the listeners.
			//   - Otherwise, do not notify listeners.
			// - If a host is removed, check for an overlapping CIDR
			//   and if it exists, notify the listeners with an upsert
			//   for the CIDR's identity
			// - If any other deletion case, notify listeners of
			//   the deletion event.
			switch event.Typ {
			case kvstore.EventTypeListDone:
				IPIdentityCache.Lock()
				for _, listener := range IPIdentityCache.listeners {
					listener.OnIPIdentityCacheGC()
				}
				IPIdentityCache.Unlock()
				close(iw.synced)

			case kvstore.EventTypeCreate, kvstore.EventTypeModify:
				var ipIDPair identity.IPIdentityPair
				err := json.Unmarshal(event.Value, &ipIDPair)
				if err != nil {
					scopedLog.WithError(err).Errorf("Not adding entry to ip cache; error unmarshaling data from key-value store")
					continue
				}
				ip := ipIDPair.PrefixString()
				if ip == "<nil>" {
					scopedLog.Debug("Ignoring entry with nil IP")
					continue
				}

				IPIdentityCache.Upsert(ipIDPair.PrefixString(), ipIDPair.HostIP, Identity{
					ID:     ipIDPair.ID,
					Source: FromKVStore,
				})

			case kvstore.EventTypeDelete:
				// Value is not present in deletion event;
				// need to convert kvstore key to IP.
				ipnet, isHost, err := keyToIPNet(event.Key)
				if err != nil {
					scopedLog.WithError(err).Error("Error parsing IP from key")
					continue
				}
				var ip string
				if isHost {
					ip = ipnet.IP.String()
				} else {
					ip = ipnet.String()
				}
				globalMap.Lock()

				if m, ok := globalMap.marshaledIPIDPairs[event.Key]; ok {
					log.WithField("ip", ip).Warning("Received kvstore delete notification for alive ipcache entry")
					err := globalMap.store.upsert(event.Key, m, true)
					if err != nil {
						log.WithError(err).WithField("ip", ip).Warning("Unable to re-create alive ipcache entry")
					}
					globalMap.Unlock()
				} else if _, ok := globalMap.keys[path.Join(IPIdentitiesPath, AddressSpace, ip)]; ok {
					log.WithField("ip", ip).Warning("Received kvstore delete notification with mismatching key but alive IP. Ignoring")
					globalMap.Unlock()
				} else {
					globalMap.Unlock()

					// The key no longer exists in the
					// local cache, it is safe to remove
					// from the datapath ipcache.
					IPIdentityCache.Delete(ip, FromKVStore)
				}
			}

		case <-iw.stop:
			// identity watcher was stopped
			watcher.Stop()
			return
		}
	}
}

// Close stops the IPIdentityWatcher and causes Watch() to return
func (iw *IPIdentityWatcher) Close() {
	iw.stopOnce.Do(func() {
		close(iw.stop)
	})
}

func (iw *IPIdentityWatcher) waitForInitialSync() {
	<-iw.synced
}

var (
	watcher     *IPIdentityWatcher
	initialized = make(chan struct{}, 0)
)

// InitIPIdentityWatcher initializes the watcher for ip-identity mapping events
// in the key-value store.
func InitIPIdentityWatcher() {
	setupIPIdentityWatcher.Do(func() {
		globalMap = newKVReferenceCounter(kvstoreImplementation{})
		log.Info("Starting IP identity watcher")
		watcher = NewIPIdentityWatcher(kvstore.Client())
		close(initialized)
		go watcher.Watch()
	})
}

// WaitForInitialSync waits until the ipcache has been synchronized from the kvstore
func WaitForInitialSync() {
	<-initialized
	watcher.waitForInitialSync()
}
<commit_msg>pkg/ipcache: do not block initialization of identity watcher<commit_after>// Copyright 2018 Authors of Cilium
//
// 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 ipcache

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

	"github.com/cilium/cilium/pkg/identity"
	"github.com/cilium/cilium/pkg/kvstore"
	"github.com/cilium/cilium/pkg/lock"
	"github.com/cilium/cilium/pkg/logging/logfields"

	"github.com/sirupsen/logrus"
)

const (
	// DefaultAddressSpace is the address space used if none is provided.
	// TODO - once pkg/node adds this to clusterConfiguration, remove.
	DefaultAddressSpace = "default"
)

var (
	// IPIdentitiesPath is the path to where endpoint IPs are stored in the key-value
	//store.
	IPIdentitiesPath = path.Join(kvstore.BaseKeyPrefix, "state", "ip", "v1")

	// AddressSpace is the address space (cluster, etc.) in which policy is
	// computed. It is determined by the orchestration system / runtime.
	AddressSpace = DefaultAddressSpace

	// globalMap wraps the kvstore and provides reference-tracking for keys
	// that are upserted or released from the kvstore.
	globalMap *kvReferenceCounter

	setupIPIdentityWatcher sync.Once
)

// store is a key-value store for an underlying implementation, provided to
// mock out the kvstore for unit testing.
type store interface {
	// update will insert the {key, value} tuple into the underlying
	// kvstore.
	upsert(key string, value []byte, lease bool) error

	// delete will remove the key from the underlying kvstore.
	release(key string) error
}

// kvstoreImplementation is a store implementation backed by the kvstore.
type kvstoreImplementation struct{}

// upsert places the mapping of {key, value} into the kvstore, optionally with
// a lease.
func (k kvstoreImplementation) upsert(key string, value []byte, lease bool) error {
	return kvstore.Update(key, value, lease)
}

// release removes the specified key from the kvstore.
func (k kvstoreImplementation) release(key string) error {
	return kvstore.Delete(key)
}

// kvReferenceCounter provides a thin wrapper around the kvstore which adds
// reference tracking for all entries being updated. When the first key is
// updated, it adds a reference to the kvstore and tracks the reference
// internally. Subsequent updates also update the kvstore, and add a reference.
// Deletes from the kvReferenceCounter are only propagated to the kvstore when
// the final reference is released.
//
// This has some small overlap with the pkg/kvstore/allocator but this is only
// a map from key to reference count rather than also tracking values.
type kvReferenceCounter struct {
	lock.Mutex
	store

	// keys is a map from key to reference count for locally-referenced
	// keys in the global kvstore.
	keys map[string]uint64

	// marshaledIPIDPair is map indexed by the key that contains the
	// marshaled IPIdentityPair
	marshaledIPIDPairs map[string][]byte
}

// newKVReferenceCounter creates a new reference counter using the specified
// store as the underlying location for key/value pairs to be stored.
func newKVReferenceCounter(s store) *kvReferenceCounter {
	return &kvReferenceCounter{
		store:              s,
		keys:               map[string]uint64{},
		marshaledIPIDPairs: map[string][]byte{},
	}
}

// upsert attempts to insert the specified {key, ipIDPair} into the kvstore. If
// the key has previously been upserted, increments a reference on the key.
// Always updates the underlying store with this {key, ipIDPair} tuple.
// Only adds a reference to the key if the upsert is successful.
func (r *kvReferenceCounter) upsert(ipKey string, ipIDPair identity.IPIdentityPair) error {
	marshaledIPIDPair, err := json.Marshal(ipIDPair)
	if err != nil {
		return err
	}

	log.WithFields(logrus.Fields{
		logfields.IPAddr:       ipIDPair.IP,
		logfields.IPMask:       ipIDPair.Mask,
		logfields.Identity:     ipIDPair.ID,
		logfields.Modification: Upsert,
	}).Debug("upserting CIDR->ID mapping to kvstore")

	r.Lock()
	defer r.Unlock()
	refcnt := r.keys[ipKey] // 0 if not found
	refcnt++
	err = r.store.upsert(ipKey, marshaledIPIDPair, true)
	if err == nil {
		r.keys[ipKey] = refcnt
		r.marshaledIPIDPairs[ipKey] = marshaledIPIDPair
	}
	return err
}

// release removes a reference to the specified key. If the number of
// references reaches 0, the key is removed from the underlying kvstore.
func (r *kvReferenceCounter) release(key string) (err error) {
	r.Lock()
	defer r.Unlock()

	refcnt, ok := r.keys[key] // 0 if not found
	// avoid underflow and report bug
	if !ok || refcnt == 0 {
		log.WithField("key", key).Error("BUG: attempt to release ipcache entry while refcnt == 0")
		return nil
	}

	refcnt--
	if refcnt == 0 {
		delete(r.keys, key)
		delete(r.marshaledIPIDPairs, key)
		err = r.store.release(key)
	} else {
		r.keys[key] = refcnt
	}
	return err
}

// UpsertIPToKVStore updates / inserts the provided IP->Identity mapping into the
// kvstore, which will subsequently trigger an event in NewIPIdentityWatcher().
func UpsertIPToKVStore(IP, hostIP net.IP, ID identity.NumericIdentity, metadata string) error {
	ipKey := path.Join(IPIdentitiesPath, AddressSpace, IP.String())
	ipIDPair := identity.IPIdentityPair{
		IP:       IP,
		ID:       ID,
		Metadata: metadata,
		HostIP:   hostIP,
	}

	marshaledIPIDPair, err := json.Marshal(ipIDPair)
	if err != nil {
		return err
	}

	log.WithFields(logrus.Fields{
		logfields.IPAddr:       ipIDPair.IP,
		logfields.Identity:     ipIDPair.ID,
		logfields.Modification: Upsert,
	}).Debug("upserting IP->ID mapping to kvstore")

	return globalMap.store.upsert(ipKey, marshaledIPIDPair, true)
}

// keyToIPNet returns the IPNet describing the key, whether it is a host, and
// an error (if one occurs)
func keyToIPNet(key string) (parsedPrefix *net.IPNet, host bool, err error) {
	requiredPrefix := fmt.Sprintf("%s/", path.Join(IPIdentitiesPath, AddressSpace))
	if !strings.HasPrefix(key, requiredPrefix) {
		err = fmt.Errorf("Found invalid key %s outside of prefix %s", key, IPIdentitiesPath)
		return
	}

	suffix := strings.TrimPrefix(key, requiredPrefix)

	// Key is formatted as "prefix/192.0.2.0/24" for CIDRs
	_, parsedPrefix, err = net.ParseCIDR(suffix)
	if err != nil {
		// Key is likely a host in the format "prefix/192.0.2.3"
		parsedIP := net.ParseIP(suffix)
		if parsedIP == nil {
			err = fmt.Errorf("unable to parse IP from suffix %s", suffix)
			return
		}
		err = nil
		host = true
		ipv4 := parsedIP.To4()
		bits := net.IPv6len * 8
		if ipv4 != nil {
			parsedIP = ipv4
			bits = net.IPv4len * 8
		}
		parsedPrefix = &net.IPNet{IP: parsedIP, Mask: net.CIDRMask(bits, bits)}
	}

	return
}

// DeleteIPFromKVStore removes the IP->Identity mapping for the specified ip
// from the kvstore, which will subsequently trigger an event in
// NewIPIdentityWatcher().
func DeleteIPFromKVStore(ip string) error {
	ipKey := path.Join(IPIdentitiesPath, AddressSpace, ip)
	return globalMap.store.release(ipKey)
}

// IPIdentityWatcher is a watcher that will notify when IP<->identity mappings
// change in the kvstore
type IPIdentityWatcher struct {
	backend  kvstore.BackendOperations
	stop     chan struct{}
	synced   chan struct{}
	stopOnce sync.Once
}

// NewIPIdentityWatcher creates a new IPIdentityWatcher using the specified
// kvstore backend
func NewIPIdentityWatcher(backend kvstore.BackendOperations) *IPIdentityWatcher {
	watcher := &IPIdentityWatcher{
		backend: backend,
		stop:    make(chan struct{}),
		synced:  make(chan struct{}),
	}

	return watcher
}

// Watch starts the watcher and blocks waiting for events. When events are
// received from the kvstore, All IPIdentityMappingListener are notified. The
// function returns when IPIdentityWatcher.Close() is called. The watcher will
// automatically restart as required.
func (iw *IPIdentityWatcher) Watch() {
restart:
	watcher := iw.backend.ListAndWatch("endpointIPWatcher", IPIdentitiesPath, 512)

	for {
		select {
		// Get events from channel as they come in.
		case event, ok := <-watcher.Events:
			if !ok {
				log.Debugf("%s closed, restarting watch", watcher.String())
				time.Sleep(500 * time.Millisecond)
				goto restart
			}

			scopedLog := log.WithFields(logrus.Fields{"kvstore-event": event.Typ.String(), "key": event.Key})
			scopedLog.Debug("Received event")

			// Synchronize local caching of endpoint IP to ipIDPair mapping with
			// operation key-value store has informed us about.
			//
			// To resolve conflicts between hosts and full CIDR prefixes:
			// - Insert hosts into the cache as ".../w.x.y.z"
			// - Insert CIDRS into the cache as ".../w.x.y.z/N"
			// - If a host entry created, notify the listeners.
			// - If a CIDR is created and there's no overlapping host
			//   entry, ie it is a less than fully masked CIDR, OR
			//   it is a fully masked CIDR and there is no corresponding
			//   host entry, then:
			//   - Notify the listeners.
			//   - Otherwise, do not notify listeners.
			// - If a host is removed, check for an overlapping CIDR
			//   and if it exists, notify the listeners with an upsert
			//   for the CIDR's identity
			// - If any other deletion case, notify listeners of
			//   the deletion event.
			switch event.Typ {
			case kvstore.EventTypeListDone:
				IPIdentityCache.Lock()
				for _, listener := range IPIdentityCache.listeners {
					listener.OnIPIdentityCacheGC()
				}
				IPIdentityCache.Unlock()
				close(iw.synced)

			case kvstore.EventTypeCreate, kvstore.EventTypeModify:
				var ipIDPair identity.IPIdentityPair
				err := json.Unmarshal(event.Value, &ipIDPair)
				if err != nil {
					scopedLog.WithError(err).Errorf("Not adding entry to ip cache; error unmarshaling data from key-value store")
					continue
				}
				ip := ipIDPair.PrefixString()
				if ip == "<nil>" {
					scopedLog.Debug("Ignoring entry with nil IP")
					continue
				}

				IPIdentityCache.Upsert(ipIDPair.PrefixString(), ipIDPair.HostIP, Identity{
					ID:     ipIDPair.ID,
					Source: FromKVStore,
				})

			case kvstore.EventTypeDelete:
				// Value is not present in deletion event;
				// need to convert kvstore key to IP.
				ipnet, isHost, err := keyToIPNet(event.Key)
				if err != nil {
					scopedLog.WithError(err).Error("Error parsing IP from key")
					continue
				}
				var ip string
				if isHost {
					ip = ipnet.IP.String()
				} else {
					ip = ipnet.String()
				}
				globalMap.Lock()

				if m, ok := globalMap.marshaledIPIDPairs[event.Key]; ok {
					log.WithField("ip", ip).Warning("Received kvstore delete notification for alive ipcache entry")
					err := globalMap.store.upsert(event.Key, m, true)
					if err != nil {
						log.WithError(err).WithField("ip", ip).Warning("Unable to re-create alive ipcache entry")
					}
					globalMap.Unlock()
				} else if _, ok := globalMap.keys[path.Join(IPIdentitiesPath, AddressSpace, ip)]; ok {
					log.WithField("ip", ip).Warning("Received kvstore delete notification with mismatching key but alive IP. Ignoring")
					globalMap.Unlock()
				} else {
					globalMap.Unlock()

					// The key no longer exists in the
					// local cache, it is safe to remove
					// from the datapath ipcache.
					IPIdentityCache.Delete(ip, FromKVStore)
				}
			}

		case <-iw.stop:
			// identity watcher was stopped
			watcher.Stop()
			return
		}
	}
}

// Close stops the IPIdentityWatcher and causes Watch() to return
func (iw *IPIdentityWatcher) Close() {
	iw.stopOnce.Do(func() {
		close(iw.stop)
	})
}

func (iw *IPIdentityWatcher) waitForInitialSync() {
	<-iw.synced
}

var (
	watcher     *IPIdentityWatcher
	initialized = make(chan struct{}, 0)
)

// InitIPIdentityWatcher initializes the watcher for ip-identity mapping events
// in the key-value store.
func InitIPIdentityWatcher() {
	setupIPIdentityWatcher.Do(func() {
		globalMap = newKVReferenceCounter(kvstoreImplementation{})
		go func() {
			log.Info("Starting IP identity watcher")
			watcher = NewIPIdentityWatcher(kvstore.Client())
			close(initialized)
			watcher.Watch()
		}()
	})
}

// WaitForInitialSync waits until the ipcache has been synchronized from the kvstore
func WaitForInitialSync() {
	<-initialized
	watcher.waitForInitialSync()
}
<|endoftext|>


<commit_before>package ca

import (
	"bytes"
	"context"
	"crypto/rand"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"fmt"
	"math/big"
	"time"

	"github.com/jetstack-experimental/cert-manager/pkg/apis/certmanager/v1alpha1"
	"github.com/jetstack-experimental/cert-manager/pkg/util/kube"
	"github.com/jetstack-experimental/cert-manager/pkg/util/pki"
	k8sErrors "k8s.io/apimachinery/pkg/api/errors"
)

const (
	errorGetCertKeyPair = "ErrGetCertKeyPair"
	errorIssueCert      = "ErrIssueCert"

	successCertIssued = "CertIssueSuccess"

	messageErrorGetCertKeyPair = "Error getting keypair for certificate: "
	messageErrorIssueCert      = "Error issuing TLS certificate: "

	messageCertIssued = "Certificate issued successfully"
)

const (
	// certificateDuration of 1 year
	certificateDuration = time.Hour * 24 * 365
	defaultOrganization = "cert-manager"
)

func (c *CA) Issue(ctx context.Context, crt *v1alpha1.Certificate) (v1alpha1.CertificateStatus, []byte, []byte, error) {
	update := crt.DeepCopy()

	signeeKey, err := kube.SecretTLSKey(c.secretsLister, crt.Namespace, crt.Spec.SecretName)

	if k8sErrors.IsNotFound(err) {
		signeeKey, err = pki.GenerateRSAPrivateKey(2048)
	}

	if err != nil {
		s := messageErrorGetCertKeyPair + err.Error()
		update.UpdateStatusCondition(v1alpha1.CertificateConditionReady, v1alpha1.ConditionFalse, errorGetCertKeyPair, s)
		return update.Status, nil, nil, err
	}

	certPem, err := c.obtainCertificate(crt, &signeeKey.PublicKey)

	if err != nil {
		s := messageErrorIssueCert + err.Error()
		update.UpdateStatusCondition(v1alpha1.CertificateConditionReady, v1alpha1.ConditionFalse, errorIssueCert, s)
		return update.Status, nil, nil, err
	}

	update.UpdateStatusCondition(v1alpha1.CertificateConditionReady, v1alpha1.ConditionTrue, successCertIssued, messageCertIssued)

	return update.Status, pki.EncodePKCS1PrivateKey(signeeKey), certPem, nil
}

func (c *CA) obtainCertificate(crt *v1alpha1.Certificate, signeeKey interface{}) ([]byte, error) {
	signerCert, err := kube.SecretTLSCert(c.secretsLister, c.resourceNamespace, c.issuer.GetSpec().CA.SecretName)

	if err != nil {
		return nil, fmt.Errorf("error getting issuer certificate: %s", err.Error())
	}

	signerKey, err := kube.SecretTLSKey(c.secretsLister, c.resourceNamespace, c.issuer.GetSpec().CA.SecretName)

	if err != nil {
		return nil, fmt.Errorf("error getting issuer private key: %s", err.Error())
	}

	crtPem, _, err := signCertificate(crt, signerCert, signeeKey, signerKey)

	if err != nil {
		return nil, err
	}

	return crtPem, nil
}

func createCertificateTemplate(publicKey interface{}, commonName string, altNames ...string) (*x509.Certificate, error) {
	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		return nil, fmt.Errorf("failed to generate serial number: %s", err.Error())
	}
	if len(commonName) == 0 && len(altNames) > 0 {
		commonName = altNames[0]
	}
	cert := &x509.Certificate{
		Version:               3,
		BasicConstraintsValid: true,
		SerialNumber:          serialNumber,
		SignatureAlgorithm:    x509.SHA256WithRSA,
		PublicKey:             publicKey,
		Subject: pkix.Name{
			Organization: []string{defaultOrganization},
			CommonName:   commonName,
		},
		NotBefore: time.Now(),
		NotAfter:  time.Now().Add(certificateDuration),
		// see http://golang.org/pkg/crypto/x509/#KeyUsage
		KeyUsage: x509.KeyUsageDigitalSignature,
		DNSNames: altNames,
	}
	return cert, nil
}

// signCertificate returns a signed x509.Certificate object for the given
// *v1alpha1.Certificate crt.
// publicKey is the public key of the signee, and signerKey is the private
// key of the signer.
func signCertificate(crt *v1alpha1.Certificate, issuerCert *x509.Certificate, publicKey interface{}, signerKey interface{}) ([]byte, *x509.Certificate, error) {
	template, err := createCertificateTemplate(publicKey, crt.Spec.CommonName, crt.Spec.AltNames...)
	if err != nil {
		return nil, nil, fmt.Errorf("error creating x509 certificate template: %s", err.Error())
	}

	derBytes, err := x509.CreateCertificate(rand.Reader, template, issuerCert, publicKey, signerKey)

	if err != nil {
		return nil, nil, fmt.Errorf("error creating x509 certificate: %s", err.Error())
	}

	cert, err := pki.DecodeDERCertificateBytes(derBytes)

	if err != nil {
		return nil, nil, fmt.Errorf("error decoding DER certificate bytes: %s", err.Error())
	}

	pemBytes := bytes.NewBuffer([]byte{})
	err = pem.Encode(pemBytes, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
	if err != nil {
		return nil, nil, fmt.Errorf("error encoding certificate PEM: %s", err.Error())
	}
	return pemBytes.Bytes(), cert, err
}
<commit_msg>Require altName or subject name are specified in CA issuer<commit_after>package ca

import (
	"bytes"
	"context"
	"crypto/rand"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"fmt"
	"math/big"
	"time"

	"github.com/jetstack-experimental/cert-manager/pkg/apis/certmanager/v1alpha1"
	"github.com/jetstack-experimental/cert-manager/pkg/util/kube"
	"github.com/jetstack-experimental/cert-manager/pkg/util/pki"
	k8sErrors "k8s.io/apimachinery/pkg/api/errors"
)

const (
	errorGetCertKeyPair = "ErrGetCertKeyPair"
	errorIssueCert      = "ErrIssueCert"

	successCertIssued = "CertIssueSuccess"

	messageErrorGetCertKeyPair = "Error getting keypair for certificate: "
	messageErrorIssueCert      = "Error issuing TLS certificate: "

	messageCertIssued = "Certificate issued successfully"
)

const (
	// certificateDuration of 1 year
	certificateDuration = time.Hour * 24 * 365
	defaultOrganization = "cert-manager"
)

func (c *CA) Issue(ctx context.Context, crt *v1alpha1.Certificate) (v1alpha1.CertificateStatus, []byte, []byte, error) {
	update := crt.DeepCopy()

	signeeKey, err := kube.SecretTLSKey(c.secretsLister, crt.Namespace, crt.Spec.SecretName)

	if k8sErrors.IsNotFound(err) {
		signeeKey, err = pki.GenerateRSAPrivateKey(2048)
	}

	if err != nil {
		s := messageErrorGetCertKeyPair + err.Error()
		update.UpdateStatusCondition(v1alpha1.CertificateConditionReady, v1alpha1.ConditionFalse, errorGetCertKeyPair, s)
		return update.Status, nil, nil, err
	}

	certPem, err := c.obtainCertificate(crt, &signeeKey.PublicKey)

	if err != nil {
		s := messageErrorIssueCert + err.Error()
		update.UpdateStatusCondition(v1alpha1.CertificateConditionReady, v1alpha1.ConditionFalse, errorIssueCert, s)
		return update.Status, nil, nil, err
	}

	update.UpdateStatusCondition(v1alpha1.CertificateConditionReady, v1alpha1.ConditionTrue, successCertIssued, messageCertIssued)

	return update.Status, pki.EncodePKCS1PrivateKey(signeeKey), certPem, nil
}

func (c *CA) obtainCertificate(crt *v1alpha1.Certificate, signeeKey interface{}) ([]byte, error) {
	commonName := crt.Spec.CommonName
	altNames := crt.Spec.AltNames
	if len(commonName) == 0 || len(altNames) == 0 {
		return nil, fmt.Errorf("no domains specified on certificate")
	}

	signerCert, err := kube.SecretTLSCert(c.secretsLister, c.resourceNamespace, c.issuer.GetSpec().CA.SecretName)
	if err != nil {
		return nil, fmt.Errorf("error getting issuer certificate: %s", err.Error())
	}

	signerKey, err := kube.SecretTLSKey(c.secretsLister, c.resourceNamespace, c.issuer.GetSpec().CA.SecretName)
	if err != nil {
		return nil, fmt.Errorf("error getting issuer private key: %s", err.Error())
	}

	crtPem, _, err := signCertificate(crt, signerCert, signeeKey, signerKey)
	if err != nil {
		return nil, err
	}

	return crtPem, nil
}

func createCertificateTemplate(publicKey interface{}, commonName string, altNames ...string) (*x509.Certificate, error) {
	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		return nil, fmt.Errorf("failed to generate serial number: %s", err.Error())
	}
	if len(commonName) == 0 && len(altNames) > 0 {
		commonName = altNames[0]
	}
	cert := &x509.Certificate{
		Version:               3,
		BasicConstraintsValid: true,
		SerialNumber:          serialNumber,
		SignatureAlgorithm:    x509.SHA256WithRSA,
		PublicKey:             publicKey,
		Subject: pkix.Name{
			Organization: []string{defaultOrganization},
			CommonName:   commonName,
		},
		NotBefore: time.Now(),
		NotAfter:  time.Now().Add(certificateDuration),
		// see http://golang.org/pkg/crypto/x509/#KeyUsage
		KeyUsage: x509.KeyUsageDigitalSignature,
		DNSNames: altNames,
	}
	return cert, nil
}

// signCertificate returns a signed x509.Certificate object for the given
// *v1alpha1.Certificate crt.
// publicKey is the public key of the signee, and signerKey is the private
// key of the signer.
func signCertificate(crt *v1alpha1.Certificate, issuerCert *x509.Certificate, publicKey interface{}, signerKey interface{}) ([]byte, *x509.Certificate, error) {
	template, err := createCertificateTemplate(publicKey, crt.Spec.CommonName, crt.Spec.AltNames...)
	if err != nil {
		return nil, nil, fmt.Errorf("error creating x509 certificate template: %s", err.Error())
	}

	derBytes, err := x509.CreateCertificate(rand.Reader, template, issuerCert, publicKey, signerKey)

	if err != nil {
		return nil, nil, fmt.Errorf("error creating x509 certificate: %s", err.Error())
	}

	cert, err := pki.DecodeDERCertificateBytes(derBytes)

	if err != nil {
		return nil, nil, fmt.Errorf("error decoding DER certificate bytes: %s", err.Error())
	}

	pemBytes := bytes.NewBuffer([]byte{})
	err = pem.Encode(pemBytes, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
	if err != nil {
		return nil, nil, fmt.Errorf("error encoding certificate PEM: %s", err.Error())
	}
	return pemBytes.Bytes(), cert, err
}
<|endoftext|>


<commit_before>package main

import (
	"testing"
	"time"

	"github.com/marcusolsson/goddd/application"
	"github.com/marcusolsson/goddd/domain/cargo"
	"github.com/marcusolsson/goddd/domain/location"
	"github.com/marcusolsson/goddd/domain/voyage"
	"github.com/marcusolsson/goddd/infrastructure"

	. "gopkg.in/check.v1"
)

func Test(t *testing.T) { TestingT(t) }

type S struct{}

var _ = Suite(&S{})

var (
	cargoRepository    = infrastructure.NewInMemCargoRepository()
	locationRepository = infrastructure.NewInMemLocationRepository()
	routingService     = &stubRoutingService{}
	bookingService     = application.NewBookingService(cargoRepository, locationRepository, routingService)
)

var (
	cargoEventHandler      = &stubCargoEventHandler{}
	cargoInspectionService = application.NewCargoInspectionService(cargoRepository, handlingEventRepository, cargoEventHandler)
)

var (
	voyageRepository = infrastructure.NewInMemVoyageRepository()
)

var (
	handlingEventRepository = infrastructure.NewInMemHandlingEventRepository()
	handlingEventFactory    = cargo.HandlingEventFactory{cargoRepository, voyageRepository, locationRepository}
	handlingEventHandler    = &stubHandlingEventHandler{cargoInspectionService}
	handlingEventService    = application.NewHandlingEventService(handlingEventRepository, handlingEventFactory, handlingEventHandler)
)

func (s *S) TestCargoFromHongkongToStockholm(chk *C) {

	var err error

	var (
		origin          = location.CNHKG // Hongkong
		destination     = location.SESTO // Stockholm
		arrivalDeadline = time.Date(2009, time.March, 18, 12, 00, 00, 00, time.UTC)
	)

	// Use case 1: booking
	trackingId, err := bookingService.BookNewCargo(origin, destination, arrivalDeadline)

	chk.Assert(err, IsNil)

	c, err := cargoRepository.Find(trackingId)

	chk.Assert(err, IsNil)
	chk.Check(c.Delivery.TransportStatus, Equals, cargo.NotReceived)
	chk.Check(c.Delivery.RoutingStatus, Equals, cargo.NotRouted)
	chk.Check(c.Delivery.IsMisdirected, Equals, false)
	chk.Check(c.Delivery.Itinerary.IsEmpty(), Equals, true)
	//chk.Check(c.Delivery.ETA, Equals, ...)
	//chk.Check(c.Delivery.NextExpectedActivity, Equals, ...)

	// Use case 2: routing
	itineraries := bookingService.RequestPossibleRoutesForCargo(trackingId)
	itinerary := selectPreferredItinerary(itineraries)

	c.AssignToRoute(itinerary)

	cargoRepository.Store(c)

	chk.Check(c.TransportStatus, Equals, cargo.NotReceived)
	chk.Check(c.RoutingStatus, Equals, cargo.Routed)
	chk.Check(c.Delivery.IsMisdirected, Equals, false)
	chk.Check(c.Delivery.Itinerary.IsEmpty(), Equals, false)
	//chk.Check(c.Delivery.ETA, Equals, ...)
	//chk.Check(c.Delivery.NextExpectedActivity, Equals, ...)

	// Use case 3: handling
	err = handlingEventService.RegisterHandlingEvent(toDate(2009, time.March, 1), trackingId, "", location.CNHKG, cargo.Receive)
	chk.Check(err, IsNil)

	// Ensure we're not working with stale cargo.
	c, err = cargoRepository.Find(trackingId)

	chk.Check(c.Delivery.TransportStatus, Equals, cargo.InPort)
	chk.Check(c.Delivery.LastKnownLocation, Equals, location.CNHKG)
	chk.Check(c.Delivery.Itinerary.IsEmpty(), Equals, false)

	err = handlingEventService.RegisterHandlingEvent(toDate(2009, time.March, 3), trackingId, voyage.V100.VoyageNumber, location.CNHKG, cargo.Load)
	chk.Check(err, IsNil)

	c, err = cargoRepository.Find(trackingId)

	//chk.Check(c.Delivery.CurrentVoyage, Equals, ...)
	chk.Check(c.Delivery.TransportStatus, Equals, cargo.OnboardCarrier)
	chk.Check(c.Delivery.LastKnownLocation, Equals, location.CNHKG)
	chk.Check(c.Delivery.IsMisdirected, Equals, false)
	chk.Check(c.Delivery.Itinerary.IsEmpty(), Equals, false)
	//chk.Check(c.Delivery.NextExpectedActivity, Equals, ...)

	// Here's an attempt to register a handling event that's not valid
	// because there is no voyage with the specified voyage number,
	// and there's no location with the specified UN Locode either.
	noSuchVoyageNumber := voyage.VoyageNumber("XX000")
	noSuchUNLocode := location.UNLocode("ZZZZZ")
	err = handlingEventService.RegisterHandlingEvent(toDate(2009, time.March, 5), trackingId, noSuchVoyageNumber, noSuchUNLocode, cargo.Load)
	chk.Check(err, NotNil)

	// Cargo is now (incorrectly) unloaded in Tokyo
	err = handlingEventService.RegisterHandlingEvent(toDate(2009, time.March, 5), trackingId, voyage.V100.VoyageNumber, location.JNTKO, cargo.Unload)
	chk.Check(err, IsNil)

	c, err = cargoRepository.Find(trackingId)

	//chk.Check(c.Delivery.CurrentVoyage, Equals, ...)
	chk.Check(c.Delivery.LastKnownLocation, Equals, location.JNTKO)
	chk.Check(c.Delivery.TransportStatus, Equals, cargo.InPort)
	chk.Check(c.Delivery.IsMisdirected, Equals, true)
	chk.Check(c.Delivery.Itinerary.IsEmpty(), Equals, false)
	//chk.Check(c.Delivery.NextExpectedActivity, Equals, ...)
}

func selectPreferredItinerary(itineraries []cargo.Itinerary) cargo.Itinerary {
	return itineraries[0]
}

func toDate(year int, month time.Month, day int) time.Time {
	return time.Date(year, month, day, 12, 00, 00, 00, time.UTC)
}

// Stub RoutingService
type stubRoutingService struct{}

func (s *stubRoutingService) FetchRoutesForSpecification(routeSpecification cargo.RouteSpecification) []cargo.Itinerary {
	if routeSpecification.Origin == location.CNHKG {
		return []cargo.Itinerary{
			cargo.Itinerary{[]cargo.Leg{
				cargo.Leg{"V100", location.CNHKG, location.USNYC, toDate(2009, time.March, 3), toDate(2009, time.March, 9)},
				cargo.Leg{"V200", location.USNYC, location.USCHI, toDate(2009, time.March, 10), toDate(2009, time.March, 14)},
				cargo.Leg{"V300", location.USCHI, location.SESTO, toDate(2009, time.March, 7), toDate(2009, time.March, 11)},
			}},
		}
	} else {
		return []cargo.Itinerary{
			cargo.Itinerary{[]cargo.Leg{
				cargo.Leg{"V300", location.JNTKO, location.DEHAM, toDate(2009, time.March, 8), toDate(2009, time.March, 12)},
				cargo.Leg{"V400", location.DEHAM, location.SESTO, toDate(2009, time.March, 14), toDate(2009, time.March, 15)},
			}},
		}
	}

}

// Stub HandlingEventHandler
type stubHandlingEventHandler struct {
	application.CargoInspectionService
}

func (h *stubHandlingEventHandler) CargoWasHandled(event cargo.HandlingEvent) {
	h.CargoInspectionService.InspectCargo(event.TrackingId)
}

// Stub CargoEventHandler
type stubCargoEventHandler struct {
}

func (h *stubCargoEventHandler) CargoWasMisdirected(c cargo.Cargo) {
}

func (h *stubCargoEventHandler) CargoHasArrived(c cargo.Cargo) {
}
<commit_msg>Test rerouting of cargo.<commit_after>package main

import (
	"testing"
	"time"

	"github.com/marcusolsson/goddd/application"
	"github.com/marcusolsson/goddd/domain/cargo"
	"github.com/marcusolsson/goddd/domain/location"
	"github.com/marcusolsson/goddd/domain/voyage"
	"github.com/marcusolsson/goddd/infrastructure"

	. "gopkg.in/check.v1"
)

func Test(t *testing.T) { TestingT(t) }

type S struct{}

var _ = Suite(&S{})

var (
	cargoRepository    = infrastructure.NewInMemCargoRepository()
	locationRepository = infrastructure.NewInMemLocationRepository()
	routingService     = &stubRoutingService{}
	bookingService     = application.NewBookingService(cargoRepository, locationRepository, routingService)
)

var (
	cargoEventHandler      = &stubCargoEventHandler{}
	cargoInspectionService = application.NewCargoInspectionService(cargoRepository, handlingEventRepository, cargoEventHandler)
)

var (
	voyageRepository = infrastructure.NewInMemVoyageRepository()
)

var (
	handlingEventRepository = infrastructure.NewInMemHandlingEventRepository()
	handlingEventFactory    = cargo.HandlingEventFactory{cargoRepository, voyageRepository, locationRepository}
	handlingEventHandler    = &stubHandlingEventHandler{cargoInspectionService}
	handlingEventService    = application.NewHandlingEventService(handlingEventRepository, handlingEventFactory, handlingEventHandler)
)

func (s *S) TestCargoFromHongkongToStockholm(chk *C) {

	var err error

	var (
		origin          = location.CNHKG // Hongkong
		destination     = location.SESTO // Stockholm
		arrivalDeadline = time.Date(2009, time.March, 18, 12, 00, 00, 00, time.UTC)
	)

	//
	// Use case 1: booking
	//

	trackingId, err := bookingService.BookNewCargo(origin, destination, arrivalDeadline)

	chk.Assert(err, IsNil)

	c, err := cargoRepository.Find(trackingId)

	chk.Assert(err, IsNil)
	chk.Check(c.Delivery.TransportStatus, Equals, cargo.NotReceived)
	chk.Check(c.Delivery.RoutingStatus, Equals, cargo.NotRouted)
	chk.Check(c.Delivery.IsMisdirected, Equals, false)
	chk.Check(c.Delivery.Itinerary.IsEmpty(), Equals, true)
	//chk.Check(c.Delivery.ETA, Equals, ...)
	//chk.Check(c.Delivery.NextExpectedActivity, Equals, ...)

	//
	// Use case 2: routing
	//

	itineraries := bookingService.RequestPossibleRoutesForCargo(trackingId)
	itinerary := selectPreferredItinerary(itineraries)

	c.AssignToRoute(itinerary)

	cargoRepository.Store(c)

	chk.Check(c.TransportStatus, Equals, cargo.NotReceived)
	chk.Check(c.RoutingStatus, Equals, cargo.Routed)
	chk.Check(c.Delivery.IsMisdirected, Equals, false)
	chk.Check(c.Delivery.Itinerary.IsEmpty(), Equals, false)
	//chk.Check(c.Delivery.ETA, Equals, ...)
	//chk.Check(c.Delivery.NextExpectedActivity, Equals, ...)

	//
	// Use case 3: handling
	//

	err = handlingEventService.RegisterHandlingEvent(toDate(2009, time.March, 1), trackingId, "", location.CNHKG, cargo.Receive)
	chk.Check(err, IsNil)

	// Ensure we're not working with stale cargo.
	c, err = cargoRepository.Find(trackingId)

	chk.Check(c.Delivery.TransportStatus, Equals, cargo.InPort)
	chk.Check(c.Delivery.LastKnownLocation, Equals, location.CNHKG)
	chk.Check(c.Delivery.Itinerary.IsEmpty(), Equals, false)

	err = handlingEventService.RegisterHandlingEvent(toDate(2009, time.March, 3), trackingId, voyage.V100.VoyageNumber, location.CNHKG, cargo.Load)
	chk.Check(err, IsNil)

	c, err = cargoRepository.Find(trackingId)

	chk.Check(c.Delivery.TransportStatus, Equals, cargo.OnboardCarrier)
	chk.Check(c.Delivery.LastKnownLocation, Equals, location.CNHKG)
	chk.Check(c.Delivery.IsMisdirected, Equals, false)
	chk.Check(c.Delivery.Itinerary.IsEmpty(), Equals, false)
	//chk.Check(c.Delivery.CurrentVoyage, Equals, ...)
	//chk.Check(c.Delivery.NextExpectedActivity, Equals, ...)

	//
	// Here's an attempt to register a handling event that's not valid
	// because there is no voyage with the specified voyage number,
	// and there's no location with the specified UN Locode either.
	//

	noSuchVoyageNumber := voyage.VoyageNumber("XX000")
	noSuchUNLocode := location.UNLocode("ZZZZZ")
	err = handlingEventService.RegisterHandlingEvent(toDate(2009, time.March, 5), trackingId, noSuchVoyageNumber, noSuchUNLocode, cargo.Load)
	chk.Check(err, NotNil)

	//
	// Cargo is incorrectly unloaded in Tokyo
	//

	err = handlingEventService.RegisterHandlingEvent(toDate(2009, time.March, 5), trackingId, voyage.V100.VoyageNumber, location.JNTKO, cargo.Unload)
	chk.Check(err, IsNil)

	c, err = cargoRepository.Find(trackingId)

	chk.Check(c.Delivery.LastKnownLocation, Equals, location.JNTKO)
	chk.Check(c.Delivery.TransportStatus, Equals, cargo.InPort)
	chk.Check(c.Delivery.Itinerary.IsEmpty(), Equals, false)
	//chk.Check(c.Delivery.CurrentVoyage, Equals, ...)
	//chk.Check(c.Delivery.NextExpectedActivity, Equals, ...)

	// Cargo is now misdirected
	chk.Check(c.Delivery.IsMisdirected, Equals, true)

	//
	// Cargo needs to be rerouted
	//

	routeSpecification := cargo.RouteSpecification{
		Origin:          location.JNTKO,
		Destination:     location.SESTO,
		ArrivalDeadline: arrivalDeadline,
	}

	c.SpecifyNewRoute(routeSpecification)

	cargoRepository.Store(c)

	chk.Check(c.RoutingStatus, Equals, cargo.Misrouted)
	//chk.Check(c.Delivery.NextExpectedActivity, Equals, ...)

	newItineraries := bookingService.RequestPossibleRoutesForCargo(trackingId)
	newItinerary := selectPreferredItinerary(newItineraries)

	c.AssignToRoute(newItinerary)

	cargoRepository.Store(c)

	chk.Check(c.Delivery.RoutingStatus, Equals, cargo.Routed)
}

func selectPreferredItinerary(itineraries []cargo.Itinerary) cargo.Itinerary {
	return itineraries[0]
}

func toDate(year int, month time.Month, day int) time.Time {
	return time.Date(year, month, day, 12, 00, 00, 00, time.UTC)
}

// Stub RoutingService
type stubRoutingService struct{}

func (s *stubRoutingService) FetchRoutesForSpecification(routeSpecification cargo.RouteSpecification) []cargo.Itinerary {
	if routeSpecification.Origin == location.CNHKG {
		return []cargo.Itinerary{
			cargo.Itinerary{[]cargo.Leg{
				cargo.Leg{"V100", location.CNHKG, location.USNYC, toDate(2009, time.March, 3), toDate(2009, time.March, 9)},
				cargo.Leg{"V200", location.USNYC, location.USCHI, toDate(2009, time.March, 10), toDate(2009, time.March, 14)},
				cargo.Leg{"V300", location.USCHI, location.SESTO, toDate(2009, time.March, 7), toDate(2009, time.March, 11)},
			}},
		}
	} else {
		return []cargo.Itinerary{
			cargo.Itinerary{[]cargo.Leg{
				cargo.Leg{"V300", location.JNTKO, location.DEHAM, toDate(2009, time.March, 8), toDate(2009, time.March, 12)},
				cargo.Leg{"V400", location.DEHAM, location.SESTO, toDate(2009, time.March, 14), toDate(2009, time.March, 15)},
			}},
		}
	}

}

// Stub HandlingEventHandler
type stubHandlingEventHandler struct {
	application.CargoInspectionService
}

func (h *stubHandlingEventHandler) CargoWasHandled(event cargo.HandlingEvent) {
	h.CargoInspectionService.InspectCargo(event.TrackingId)
}

// Stub CargoEventHandler
type stubCargoEventHandler struct {
}

func (h *stubCargoEventHandler) CargoWasMisdirected(c cargo.Cargo) {
}

func (h *stubCargoEventHandler) CargoHasArrived(c cargo.Cargo) {
}
<|endoftext|>


<commit_before>package kubecli

import (
	"flag"
	"github.com/go-kit/kit/log/levels"
	"k8s.io/client-go/1.5/kubernetes"
	"k8s.io/client-go/1.5/pkg/api"
	"k8s.io/client-go/1.5/pkg/fields"
	"k8s.io/client-go/1.5/pkg/labels"
	"k8s.io/client-go/1.5/pkg/runtime"
	"k8s.io/client-go/1.5/pkg/runtime/serializer"
	"k8s.io/client-go/1.5/pkg/watch"
	"k8s.io/client-go/1.5/rest"
	"k8s.io/client-go/1.5/tools/cache"
	"k8s.io/client-go/1.5/tools/clientcmd"
	"kubevirt.io/kubevirt/pkg/api/v1"
	"runtime/debug"
	"time"
)

var (
	kubeconfig string
)

func init() {
	flag.StringVar(&kubeconfig, "kubeconfig", "", "absolute path to the kubeconfig file")
}

func Get() (*kubernetes.Clientset, error) {

	config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
	if err != nil {
		return nil, err
	}

	config.GroupVersion = &v1.GroupVersion
	config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: api.Codecs}
	config.APIPath = "/apis"
	config.ContentType = runtime.ContentTypeJSON

	return kubernetes.NewForConfig(config)
}

func GetRESTClient() (*rest.RESTClient, error) {

	config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
	if err != nil {
		return nil, err
	}

	config.GroupVersion = &v1.GroupVersion
	config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: api.Codecs}
	config.APIPath = "/apis"
	config.ContentType = runtime.ContentTypeJSON

	return rest.RESTClientFor(config)
}

// NewListWatchFromClient creates a new ListWatch from the specified client, resource, namespace and field selector.
func NewListWatchFromClient(c cache.Getter, resource string, namespace string, fieldSelector fields.Selector, labelSelector labels.Selector) *cache.ListWatch {
	listFunc := func(options api.ListOptions) (runtime.Object, error) {
		return c.Get().
			Namespace(namespace).
			Resource(resource).
			VersionedParams(&options, api.ParameterCodec).
			FieldsSelectorParam(fieldSelector).
			LabelsSelectorParam(labelSelector).
			Do().
			Get()
	}
	watchFunc := func(options api.ListOptions) (watch.Interface, error) {
		return c.Get().
			Prefix("watch").
			Namespace(namespace).
			Resource(resource).
			VersionedParams(&options, api.ParameterCodec).
			FieldsSelectorParam(fieldSelector).
			LabelsSelectorParam(labelSelector).
			Watch()
	}
	return &cache.ListWatch{ListFunc: listFunc, WatchFunc: watchFunc}
}

func NewInformer(
	lw cache.ListerWatcher,
	objType runtime.Object,
	resyncPeriod time.Duration,
	h ResourceEventHandler,
) (cache.Indexer, *cache.Controller) {
	clientState := cache.NewIndexer(cache.DeletionHandlingMetaNamespaceKeyFunc, cache.Indexers{})
	fifo := cache.NewDeltaFIFO(cache.MetaNamespaceKeyFunc, nil, clientState)

	cfg := &cache.Config{
		Queue:            fifo,
		ListerWatcher:    lw,
		ObjectType:       objType,
		FullResyncPeriod: resyncPeriod,
		RetryOnError:     false,

		Process: func(obj interface{}) error {
			// from oldest to newest
			for _, d := range obj.(cache.Deltas) {
				switch d.Type {
				case cache.Sync, cache.Added, cache.Updated:
					if old, exists, err := clientState.Get(d.Object); err == nil && exists {
						if err := clientState.Update(d.Object); err != nil {
							return err
						}
						err = h.OnUpdate(old, d.Object)
						if err != nil {
							// TODO real backoff strategy
							// TODO solve this by using workqueues as soon as they hit client-go
							time.Sleep(1 * time.Second)
							return err
						}
					} else {
						if err := clientState.Add(d.Object); err != nil {
							// TODO real backoff strategy
							// TODO solve this by using workqueues as soon as they hit client-go
							time.Sleep(1 * time.Second)
							return err
						}
						err = h.OnAdd(d.Object)
						if err != nil {
							// TODO real backoff strategy
							// TODO solve this by using workqueues as soon as they hit client-go
							time.Sleep(1 * time.Second)
							return err
						}
					}
				case cache.Deleted:
					if err := clientState.Delete(d.Object); err != nil {
						// TODO real backoff strategy
						// TODO solve this by using workqueues as soon as they hit client-go
						time.Sleep(1 * time.Second)
						return err
					}
					err := h.OnDelete(d.Object)
					if err != nil {
						// TODO real backoff strategy
						// TODO solve this by using workqueues as soon as they hit client-go
						time.Sleep(1 * time.Second)
						return err
					}
				}
			}
			return nil
		},
	}
	return clientState, cache.New(cfg)
}

type ResourceEventHandler interface {
	OnAdd(obj interface{}) error
	OnUpdate(oldObj, newObj interface{}) error
	OnDelete(obj interface{}) error
}

func NewPanicCatcher(logger levels.Levels) func() {
	return func() {
		if r := recover(); r != nil {
			logger.Crit().Log("stacktrace", debug.Stack(), "msg", r)
		}
	}
}

type ResourceEventHandlerFuncs struct {
	AddFunc    func(obj interface{}) error
	UpdateFunc func(oldObj, newObj interface{}) error
	DeleteFunc func(obj interface{}) error
}

func (r ResourceEventHandlerFuncs) OnAdd(obj interface{}) error {
	return r.AddFunc(obj)
}

func (r ResourceEventHandlerFuncs) OnUpdate(oldObj, newObj interface{}) error {
	return r.UpdateFunc(oldObj, newObj)
}

func (r ResourceEventHandlerFuncs) OnDelete(obj interface{}) error {
	return r.DeleteFunc(obj)
}
<commit_msg>Only update the VM cache if the event handling succeeded<commit_after>package kubecli

import (
	"flag"
	"github.com/go-kit/kit/log/levels"
	"k8s.io/client-go/1.5/kubernetes"
	"k8s.io/client-go/1.5/pkg/api"
	"k8s.io/client-go/1.5/pkg/fields"
	"k8s.io/client-go/1.5/pkg/labels"
	"k8s.io/client-go/1.5/pkg/runtime"
	"k8s.io/client-go/1.5/pkg/runtime/serializer"
	"k8s.io/client-go/1.5/pkg/watch"
	"k8s.io/client-go/1.5/rest"
	"k8s.io/client-go/1.5/tools/cache"
	"k8s.io/client-go/1.5/tools/clientcmd"
	"kubevirt.io/kubevirt/pkg/api/v1"
	"runtime/debug"
	"time"
)

var (
	kubeconfig string
)

func init() {
	flag.StringVar(&kubeconfig, "kubeconfig", "", "absolute path to the kubeconfig file")
}

func Get() (*kubernetes.Clientset, error) {

	config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
	if err != nil {
		return nil, err
	}

	config.GroupVersion = &v1.GroupVersion
	config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: api.Codecs}
	config.APIPath = "/apis"
	config.ContentType = runtime.ContentTypeJSON

	return kubernetes.NewForConfig(config)
}

func GetRESTClient() (*rest.RESTClient, error) {

	config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
	if err != nil {
		return nil, err
	}

	config.GroupVersion = &v1.GroupVersion
	config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: api.Codecs}
	config.APIPath = "/apis"
	config.ContentType = runtime.ContentTypeJSON

	return rest.RESTClientFor(config)
}

// NewListWatchFromClient creates a new ListWatch from the specified client, resource, namespace and field selector.
func NewListWatchFromClient(c cache.Getter, resource string, namespace string, fieldSelector fields.Selector, labelSelector labels.Selector) *cache.ListWatch {
	listFunc := func(options api.ListOptions) (runtime.Object, error) {
		return c.Get().
			Namespace(namespace).
			Resource(resource).
			VersionedParams(&options, api.ParameterCodec).
			FieldsSelectorParam(fieldSelector).
			LabelsSelectorParam(labelSelector).
			Do().
			Get()
	}
	watchFunc := func(options api.ListOptions) (watch.Interface, error) {
		return c.Get().
			Prefix("watch").
			Namespace(namespace).
			Resource(resource).
			VersionedParams(&options, api.ParameterCodec).
			FieldsSelectorParam(fieldSelector).
			LabelsSelectorParam(labelSelector).
			Watch()
	}
	return &cache.ListWatch{ListFunc: listFunc, WatchFunc: watchFunc}
}

func NewInformer(
	lw cache.ListerWatcher,
	objType runtime.Object,
	resyncPeriod time.Duration,
	h ResourceEventHandler,
) (cache.Indexer, *cache.Controller) {
	clientState := cache.NewIndexer(cache.DeletionHandlingMetaNamespaceKeyFunc, cache.Indexers{})
	fifo := cache.NewDeltaFIFO(cache.MetaNamespaceKeyFunc, nil, clientState)

	cfg := &cache.Config{
		Queue:            fifo,
		ListerWatcher:    lw,
		ObjectType:       objType,
		FullResyncPeriod: resyncPeriod,
		RetryOnError:     false,

		Process: func(obj interface{}) error {
			// from oldest to newest
			for _, d := range obj.(cache.Deltas) {
				switch d.Type {
				case cache.Sync, cache.Added, cache.Updated:
					if old, exists, err := clientState.Get(d.Object); err == nil && exists {
						err = h.OnUpdate(old, d.Object)
						if err != nil {
							// TODO real backoff strategy
							// TODO solve this by using workqueues as soon as they hit client-go
							time.Sleep(1 * time.Second)
							return err
						}
						if err := clientState.Update(d.Object); err != nil {
							return err
						}
					} else {
						err = h.OnAdd(d.Object)
						if err != nil {
							// TODO real backoff strategy
							// TODO solve this by using workqueues as soon as they hit client-go
							time.Sleep(1 * time.Second)
							return err
						}
						if err := clientState.Add(d.Object); err != nil {
							return err
						}
					}
				case cache.Deleted:
					err := h.OnDelete(d.Object)
					if err != nil {
						// TODO real backoff strategy
						// TODO solve this by using workqueues as soon as they hit client-go
						time.Sleep(1 * time.Second)
						return err
					}
					if err := clientState.Delete(d.Object); err != nil {
						return err
					}
				}
			}
			return nil
		},
	}
	return clientState, cache.New(cfg)
}

type ResourceEventHandler interface {
	OnAdd(obj interface{}) error
	OnUpdate(oldObj, newObj interface{}) error
	OnDelete(obj interface{}) error
}

func NewPanicCatcher(logger levels.Levels) func() {
	return func() {
		if r := recover(); r != nil {
			logger.Crit().Log("stacktrace", debug.Stack(), "msg", r)
		}
	}
}

type ResourceEventHandlerFuncs struct {
	AddFunc    func(obj interface{}) error
	UpdateFunc func(oldObj, newObj interface{}) error
	DeleteFunc func(obj interface{}) error
}

func (r ResourceEventHandlerFuncs) OnAdd(obj interface{}) error {
	return r.AddFunc(obj)
}

func (r ResourceEventHandlerFuncs) OnUpdate(oldObj, newObj interface{}) error {
	return r.UpdateFunc(oldObj, newObj)
}

func (r ResourceEventHandlerFuncs) OnDelete(obj interface{}) error {
	return r.DeleteFunc(obj)
}
<|endoftext|>