<commit_before>package etcd_test

import (
	"testing"

	"github.com/Aptomi/aptomi/pkg/engine"
	"github.com/Aptomi/aptomi/pkg/runtime"
	"github.com/Aptomi/aptomi/pkg/runtime/store"
	"github.com/Aptomi/aptomi/pkg/runtime/store/etcd"
	"github.com/stretchr/testify/assert"
)

func TestEtcdStoreBaseFunctionality(t *testing.T) {
	// todo create helper in future that'll allow to run any number of tests in parallel - some random constant generated on start + test name as prefix
	cfg := etcd.Config{Prefix: t.Name()}
	etcdStore, err := etcd.New(cfg, runtime.NewTypes().Append(engine.TypeRevision), store.NewJsonCodec())
	assert.NoError(t, err)
	assert.NotNil(t, etcdStore)

	revision := &engine.Revision{
		TypeKind: engine.TypeRevision.GetTypeKind(),
		Metadata: runtime.GenerationMetadata{
			Generation: 1,
		},
		PolicyGen: 42,
		Status:    engine.RevisionStatusWaiting,
	}

	var changed bool
	changed, err = etcdStore.Save(revision)
	assert.NoError(t, err)
	assert.True(t, changed)
	assert.EqualValues(t, revision.GetGeneration(), 1)

	revision.Status = engine.RevisionStatusInProgress
	changed, err = etcdStore.Save(revision)
	assert.NoError(t, err)
	assert.True(t, changed)
	assert.EqualValues(t, revision.GetGeneration(), 2)

	changed, err = etcdStore.Save(revision)
	assert.NoError(t, err)
	assert.False(t, changed)
	assert.EqualValues(t, revision.GetGeneration(), 2)

	var loadedRevisions []*engine.Revision
	err = etcdStore.Find(engine.TypeRevision.Kind, &loadedRevisions, store.WithKey(engine.RevisionKey), store.WithWhereEq("Status", engine.RevisionStatusWaiting, engine.RevisionStatusInProgress))
	assert.NoError(t, err)
	assert.Len(t, loadedRevisions, 2)
	assert.NotNil(t, loadedRevisions[0])
	assert.NotNil(t, loadedRevisions[1])
	assert.Equal(t, engine.RevisionStatusWaiting, loadedRevisions[0].Status)
	assert.EqualValues(t, 1, loadedRevisions[0].GetGeneration())
	assert.Equal(t, engine.RevisionStatusInProgress, loadedRevisions[1].Status)
	assert.EqualValues(t, 2, loadedRevisions[1].GetGeneration())

	var loadedRevisionByLastGen *engine.Revision
	err = etcdStore.Find(engine.TypeRevision.Kind, &loadedRevisionByLastGen, store.WithKey(engine.RevisionKey), store.WithGen(runtime.LastOrEmptyGen))
	assert.NoError(t, err)
	assert.Equal(t, revision, loadedRevisionByLastGen)

	var loadedRevisionBySpecificGen *engine.Revision
	err = etcdStore.Find(engine.TypeRevision.Kind, &loadedRevisionBySpecificGen, store.WithKey(engine.RevisionKey), store.WithGen(2))
	assert.NoError(t, err)
	assert.Equal(t, revision, loadedRevisionBySpecificGen)

	err = etcdStore.Find(engine.TypeRevision.Kind, &loadedRevisionBySpecificGen, store.WithKey(engine.RevisionKey), store.WithGen(42))
	assert.NoError(t, err)
	assert.Nil(t, loadedRevisionBySpecificGen)
}
<commit_msg>Use provided etcd endpoints in etcd test<commit_after>package etcd_test

import (
	"os"
	"strings"
	"testing"

	"github.com/Aptomi/aptomi/pkg/engine"
	"github.com/Aptomi/aptomi/pkg/runtime"
	"github.com/Aptomi/aptomi/pkg/runtime/store"
	"github.com/Aptomi/aptomi/pkg/runtime/store/etcd"
	"github.com/stretchr/testify/assert"
)

func TestEtcdStoreBaseFunctionality(t *testing.T) {
	// todo create helper in future that'll allow to run any number of tests in parallel - some random constant generated on start + test name as prefix
	endpoints := os.Getenv("APTOMI_TEST_DB_ENDPOINTS")
	if endpoints == "" {
		endpoints = "127.0.0.1:2379"
	}
	cfg := etcd.Config{
		Prefix:    t.Name(),
		Endpoints: strings.Split(endpoints, ","),
	}
	etcdStore, err := etcd.New(cfg, runtime.NewTypes().Append(engine.TypeRevision), store.NewJSONCodec())
	assert.NoError(t, err)
	assert.NotNil(t, etcdStore)

	revision := &engine.Revision{
		TypeKind: engine.TypeRevision.GetTypeKind(),
		Metadata: runtime.GenerationMetadata{
			Generation: 1,
		},
		PolicyGen: 42,
		Status:    engine.RevisionStatusWaiting,
	}

	var changed bool
	changed, err = etcdStore.Save(revision)
	assert.NoError(t, err)
	assert.True(t, changed)
	assert.EqualValues(t, revision.GetGeneration(), 1)

	revision.Status = engine.RevisionStatusInProgress
	changed, err = etcdStore.Save(revision)
	assert.NoError(t, err)
	assert.True(t, changed)
	assert.EqualValues(t, revision.GetGeneration(), 2)

	changed, err = etcdStore.Save(revision)
	assert.NoError(t, err)
	assert.False(t, changed)
	assert.EqualValues(t, revision.GetGeneration(), 2)

	var loadedRevisions []*engine.Revision
	err = etcdStore.Find(engine.TypeRevision.Kind, &loadedRevisions, store.WithKey(engine.RevisionKey), store.WithWhereEq("Status", engine.RevisionStatusWaiting, engine.RevisionStatusInProgress))
	assert.NoError(t, err)
	assert.Len(t, loadedRevisions, 2)
	assert.NotNil(t, loadedRevisions[0])
	assert.NotNil(t, loadedRevisions[1])
	assert.Equal(t, engine.RevisionStatusWaiting, loadedRevisions[0].Status)
	assert.EqualValues(t, 1, loadedRevisions[0].GetGeneration())
	assert.Equal(t, engine.RevisionStatusInProgress, loadedRevisions[1].Status)
	assert.EqualValues(t, 2, loadedRevisions[1].GetGeneration())

	var loadedRevisionByLastGen *engine.Revision
	err = etcdStore.Find(engine.TypeRevision.Kind, &loadedRevisionByLastGen, store.WithKey(engine.RevisionKey), store.WithGen(runtime.LastOrEmptyGen))
	assert.NoError(t, err)
	assert.Equal(t, revision, loadedRevisionByLastGen)

	var loadedRevisionBySpecificGen *engine.Revision
	err = etcdStore.Find(engine.TypeRevision.Kind, &loadedRevisionBySpecificGen, store.WithKey(engine.RevisionKey), store.WithGen(2))
	assert.NoError(t, err)
	assert.Equal(t, revision, loadedRevisionBySpecificGen)

	err = etcdStore.Find(engine.TypeRevision.Kind, &loadedRevisionBySpecificGen, store.WithKey(engine.RevisionKey), store.WithGen(42))
	assert.NoError(t, err)
	assert.Nil(t, loadedRevisionBySpecificGen)
}
<|endoftext|>


<commit_before>package payment_test

import (
	"database/sql"
	"github.com/fritzpay/paymentd/pkg/paymentd/payment"
	"github.com/fritzpay/paymentd/pkg/paymentd/payment_method"
	"github.com/fritzpay/paymentd/pkg/paymentd/project"
	"github.com/fritzpay/paymentd/pkg/service"
	paymentService "github.com/fritzpay/paymentd/pkg/service/payment"
	"github.com/fritzpay/paymentd/pkg/testutil"
	. "github.com/smartystreets/goconvey/convey"
	"gopkg.in/inconshreveable/log15.v2"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"
)

func WithService(ctx *service.Context, f func(s *paymentService.Service)) func() {
	return func() {
		s, err := paymentService.NewService(ctx)
		So(err, ShouldBeNil)

		f(s)
	}
}

func WithPayment(tx *sql.Tx, f func(p *payment.Payment)) func() {
	return func() {
		pr := &project.Project{
			ID:          1,
			PrincipalID: 1,
		}
		method, err := payment_method.PaymentMethodByIDTx(tx, 1)
		So(err, ShouldBeNil)
		So(method.Active(), ShouldBeTrue)
		So(method.ID, ShouldNotEqual, 0)

		err = payment_method.InsertPaymentMethodStatusTx(tx, method)
		So(err, ShouldBeNil)

		p := &payment.Payment{
			Created:  time.Now(),
			Ident:    "test001",
			Amount:   1234,
			Subunits: 2,
			Currency: "EUR",
		}
		err = p.SetProject(pr)
		So(err, ShouldBeNil)
		p.Config.SetCountry("DE")
		p.Config.SetLocale("en-US")
		p.Config.SetPaymentMethodID(method.ID)

		err = payment.InsertPaymentTx(tx, p)
		So(err, ShouldBeNil)
		err = payment.InsertPaymentConfigTx(tx, p)
		So(err, ShouldBeNil)

		p, err = payment.PaymentByIDTx(tx, p.PaymentID())
		So(err, ShouldBeNil)

		f(p)
	}
}

func TestPaymentNotification(t *testing.T) {
	Convey("Given a payment db connection", t, testutil.WithPaymentDB(t, func(db *sql.DB) {
		Convey("Given a principal db connection", testutil.WithPrincipalDB(t, func(principalDB *sql.DB) {
			Convey("Given a transaction", func() {
				tx, err := db.Begin()
				So(err, ShouldBeNil)
				Reset(func() {
					err = tx.Rollback()
					So(err, ShouldBeNil)
				})

				Convey("Given a service context", testutil.WithContext(func(ctx *service.Context, logs <-chan *log15.Record) {
					ctx.SetPaymentDB(db, nil)
					ctx.SetPrincipalDB(principalDB, nil)

					Convey("Given a payment service", WithService(ctx, func(s *paymentService.Service) {

						Convey("Given a payment", WithPayment(tx, func(p *payment.Payment) {

							Convey("Given a test HTTP server", func() {
								srvOk := make(chan struct{})
								var req *http.Request
								var body []byte
								testSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
									req = r
									body, err = ioutil.ReadAll(r.Body)
									So(err, ShouldBeNil)
									close(srvOk)
								}))

								Reset(func() {
									testSrv.Close()
								})

								Convey("Given a test callback configuration", func() {
									testPk, err := project.ProjectKeyByKeyDB(principalDB, "testkey")
									So(err, ShouldBeNil)
									So(testPk.IsValid(), ShouldBeTrue)

									p.Config.SetCallbackURL(testSrv.URL)
									p.Config.SetCallbackAPIVersion("2")
									p.Config.SetCallbackProjectKey(testPk.Key)

									So(paymentService.CanCallback(&p.Config), ShouldBeTrue)

									Convey("When the payment has no transaction", func() {
										paymentTx, err := s.PaymentTransaction(tx, p)
										So(err, ShouldEqual, payment.ErrPaymentTransactionNotFound)
										So(paymentTx.Status.Valid(), ShouldBeFalse)

										Convey("When creating a transaction", func() {
											So(s.IsProcessablePayment(p), ShouldBeTrue)
											paymentTx = p.NewTransaction(payment.PaymentStatusOpen)
											err = s.SetPaymentTransaction(tx, paymentTx)

											Convey("It should succeed", func() {
												So(err, ShouldBeNil)
											})

											// Wrong implementation, notification should not occur inside a transaction
											//
											// 	Convey("A notification should be sent", func() {
											// 		select {
											// 		case <-srvOk:
											// 			So(req, ShouldNotBeNil)
											// 		case <-time.After(time.Second):
											// 			t.Errorf("request timeout on %s", testSrv.URL)
											// 			close(srvOk)
											// 		drain:
											// 			for {
											// 				select {
											// 				case msg := <-logs:
											// 					t.Logf("%v", msg)
											// 				default:
											// 					break drain
											// 				}
											// 			}
											// 		}

											// 		Convey("The notification should contain the transaction", func() {
											// 			not := &notification.Notification{}
											// 			dec := json.NewDecoder(bytes.NewBuffer(body))
											// 			err := dec.Decode(not)
											// 			So(err, ShouldBeNil)

											// 			So(not.TransactionTimestamp, ShouldNotEqual, 0)
											// 			So(not.Status, ShouldEqual, payment.PaymentStatusOpen)
											// 		})
											// 	})
										})
									})
								})
							})
						}))
					}))
				}))
			})
		}))
	}))
}
<commit_msg>beautify imports<commit_after>package payment_test

import (
	"database/sql"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/fritzpay/paymentd/pkg/paymentd/payment"
	"github.com/fritzpay/paymentd/pkg/paymentd/payment_method"
	"github.com/fritzpay/paymentd/pkg/paymentd/project"
	"github.com/fritzpay/paymentd/pkg/service"
	paymentService "github.com/fritzpay/paymentd/pkg/service/payment"
	"github.com/fritzpay/paymentd/pkg/testutil"
	. "github.com/smartystreets/goconvey/convey"
	"gopkg.in/inconshreveable/log15.v2"
)

func WithService(ctx *service.Context, f func(s *paymentService.Service)) func() {
	return func() {
		s, err := paymentService.NewService(ctx)
		So(err, ShouldBeNil)

		f(s)
	}
}

func WithPayment(tx *sql.Tx, f func(p *payment.Payment)) func() {
	return func() {
		pr := &project.Project{
			ID:          1,
			PrincipalID: 1,
		}
		method, err := payment_method.PaymentMethodByIDTx(tx, 1)
		So(err, ShouldBeNil)
		So(method.Active(), ShouldBeTrue)
		So(method.ID, ShouldNotEqual, 0)

		err = payment_method.InsertPaymentMethodStatusTx(tx, method)
		So(err, ShouldBeNil)

		p := &payment.Payment{
			Created:  time.Now(),
			Ident:    "test001",
			Amount:   1234,
			Subunits: 2,
			Currency: "EUR",
		}
		err = p.SetProject(pr)
		So(err, ShouldBeNil)
		p.Config.SetCountry("DE")
		p.Config.SetLocale("en-US")
		p.Config.SetPaymentMethodID(method.ID)

		err = payment.InsertPaymentTx(tx, p)
		So(err, ShouldBeNil)
		err = payment.InsertPaymentConfigTx(tx, p)
		So(err, ShouldBeNil)

		p, err = payment.PaymentByIDTx(tx, p.PaymentID())
		So(err, ShouldBeNil)

		f(p)
	}
}

func TestPaymentNotification(t *testing.T) {
	Convey("Given a payment db connection", t, testutil.WithPaymentDB(t, func(db *sql.DB) {
		Convey("Given a principal db connection", testutil.WithPrincipalDB(t, func(principalDB *sql.DB) {
			Convey("Given a transaction", func() {
				tx, err := db.Begin()
				So(err, ShouldBeNil)
				Reset(func() {
					err = tx.Rollback()
					So(err, ShouldBeNil)
				})

				Convey("Given a service context", testutil.WithContext(func(ctx *service.Context, logs <-chan *log15.Record) {
					ctx.SetPaymentDB(db, nil)
					ctx.SetPrincipalDB(principalDB, nil)

					Convey("Given a payment service", WithService(ctx, func(s *paymentService.Service) {

						Convey("Given a payment", WithPayment(tx, func(p *payment.Payment) {

							Convey("Given a test HTTP server", func() {
								srvOk := make(chan struct{})
								var req *http.Request
								var body []byte
								testSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
									req = r
									body, err = ioutil.ReadAll(r.Body)
									So(err, ShouldBeNil)
									close(srvOk)
								}))

								Reset(func() {
									testSrv.Close()
								})

								Convey("Given a test callback configuration", func() {
									testPk, err := project.ProjectKeyByKeyDB(principalDB, "testkey")
									So(err, ShouldBeNil)
									So(testPk.IsValid(), ShouldBeTrue)

									p.Config.SetCallbackURL(testSrv.URL)
									p.Config.SetCallbackAPIVersion("2")
									p.Config.SetCallbackProjectKey(testPk.Key)

									So(paymentService.CanCallback(&p.Config), ShouldBeTrue)

									Convey("When the payment has no transaction", func() {
										paymentTx, err := s.PaymentTransaction(tx, p)
										So(err, ShouldEqual, payment.ErrPaymentTransactionNotFound)
										So(paymentTx.Status.Valid(), ShouldBeFalse)

										Convey("When creating a transaction", func() {
											So(s.IsProcessablePayment(p), ShouldBeTrue)
											paymentTx = p.NewTransaction(payment.PaymentStatusOpen)
											err = s.SetPaymentTransaction(tx, paymentTx)

											Convey("It should succeed", func() {
												So(err, ShouldBeNil)
											})

											// Wrong implementation, notification should not occur inside a transaction
											//
											// 	Convey("A notification should be sent", func() {
											// 		select {
											// 		case <-srvOk:
											// 			So(req, ShouldNotBeNil)
											// 		case <-time.After(time.Second):
											// 			t.Errorf("request timeout on %s", testSrv.URL)
											// 			close(srvOk)
											// 		drain:
											// 			for {
											// 				select {
											// 				case msg := <-logs:
											// 					t.Logf("%v", msg)
											// 				default:
											// 					break drain
											// 				}
											// 			}
											// 		}

											// 		Convey("The notification should contain the transaction", func() {
											// 			not := &notification.Notification{}
											// 			dec := json.NewDecoder(bytes.NewBuffer(body))
											// 			err := dec.Decode(not)
											// 			So(err, ShouldBeNil)

											// 			So(not.TransactionTimestamp, ShouldNotEqual, 0)
											// 			So(not.Status, ShouldEqual, payment.PaymentStatusOpen)
											// 		})
											// 	})
										})
									})
								})
							})
						}))
					}))
				}))
			})
		}))
	}))
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Skaffold 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 constants

import (
	"github.com/sirupsen/logrus"
)

const (
	// DefaultLogLevel is the default global verbosity
	DefaultLogLevel = logrus.WarnLevel

	// The dockerfile path is given relative to the context directory
	DefaultDockerfilePath = "Dockerfile"

	DefaultDevTagStrategy = TagStrategySha256
	DefaultRunTagStrategy = TagStrategyGitCommit

	// TagStrategySha256 uses the checksum of the built artifact as the tag
	TagStrategySha256    = "sha256"
	TagStrategyGitCommit = "gitCommit"

	DefaultMinikubeContext         = "minikube"
	DefaultDockerForDesktopContext = "docker-for-desktop"
	GCSBucketSuffix                = "_cloudbuild"

	// DefaultKanikoImage is v0.1.0
	DefaultKanikoImage = "gcr.io/kaniko-project/executor:v0.1.0@sha256:501056bf52f3a96f151ccbeb028715330d5d5aa6647e7572ce6c6c55f91ab374"
)
<commit_msg>constants: add default kustomize path<commit_after>/*
Copyright 2018 The Skaffold 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 constants

import (
	"github.com/sirupsen/logrus"
)

const (
	// DefaultLogLevel is the default global verbosity
	DefaultLogLevel = logrus.WarnLevel

	// The dockerfile path is given relative to the context directory
	DefaultDockerfilePath = "Dockerfile"

	DefaultDevTagStrategy = TagStrategySha256
	DefaultRunTagStrategy = TagStrategyGitCommit

	// TagStrategySha256 uses the checksum of the built artifact as the tag
	TagStrategySha256    = "sha256"
	TagStrategyGitCommit = "gitCommit"

	DefaultMinikubeContext         = "minikube"
	DefaultDockerForDesktopContext = "docker-for-desktop"
	GCSBucketSuffix                = "_cloudbuild"

	DefaultKustomizationPath = "."

	// DefaultKanikoImage is v0.1.0
	DefaultKanikoImage = "gcr.io/kaniko-project/executor:v0.1.0@sha256:501056bf52f3a96f151ccbeb028715330d5d5aa6647e7572ce6c6c55f91ab374"
)
<|endoftext|>


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

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"

	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	"k8s.io/apimachinery/pkg/util/sets"
)

// Error is an implementation of the 'error' interface, which represents a
// field-level validation error.
type Error struct {
	Type     ErrorType
	Field    string
	BadValue interface{}
	Detail   string
}

var _ error = &Error{}

// Error implements the error interface.
func (v *Error) Error() string {
	return fmt.Sprintf("%s: %s", v.Field, v.ErrorBody())
}

// ErrorBody returns the error message without the field name.  This is useful
// for building nice-looking higher-level error reporting.
func (v *Error) ErrorBody() string {
	var s string
	switch v.Type {
	case ErrorTypeRequired, ErrorTypeForbidden, ErrorTypeTooLong, ErrorTypeInternal:
		s = v.Type.String()
	default:
		value := v.BadValue
		valueType := reflect.TypeOf(value)
		if value == nil || valueType == nil {
			value = "null"
		} else if valueType.Kind() == reflect.Ptr {
			if reflectValue := reflect.ValueOf(value); reflectValue.IsNil() {
				value = "null"
			} else {
				value = reflectValue.Elem().Interface()
			}
		}
		switch t := value.(type) {
		case int64, int32, float64, float32, bool:
			// use simple printer for simple types
			s = fmt.Sprintf("%s: %v", v.Type, value)
		case string:
			s = fmt.Sprintf("%s: %q", v.Type, t)
		case fmt.Stringer:
			// anything that defines String() is better than raw struct
			s = fmt.Sprintf("%s: %s", v.Type, t.String())
		default:
			// fallback to raw struct
			// TODO: internal types have panic guards against json.Marshalling to prevent
			// accidental use of internal types in external serialized form.  For now, use
			// %#v, although it would be better to show a more expressive output in the future
			s = fmt.Sprintf("%s: %#v", v.Type, value)
		}
	}
	if len(v.Detail) != 0 {
		s += fmt.Sprintf(": %s", v.Detail)
	}
	return s
}

// ErrorType is a machine readable value providing more detail about why
// a field is invalid.  These values are expected to match 1-1 with
// CauseType in api/types.go.
type ErrorType string

// TODO: These values are duplicated in api/types.go, but there's a circular dep.  Fix it.
const (
	// ErrorTypeNotFound is used to report failure to find a requested value
	// (e.g. looking up an ID).  See NotFound().
	ErrorTypeNotFound ErrorType = "FieldValueNotFound"
	// ErrorTypeRequired is used to report required values that are not
	// provided (e.g. empty strings, null values, or empty arrays).  See
	// Required().
	ErrorTypeRequired ErrorType = "FieldValueRequired"
	// ErrorTypeDuplicate is used to report collisions of values that must be
	// unique (e.g. unique IDs).  See Duplicate().
	ErrorTypeDuplicate ErrorType = "FieldValueDuplicate"
	// ErrorTypeInvalid is used to report malformed values (e.g. failed regex
	// match, too long, out of bounds).  See Invalid().
	ErrorTypeInvalid ErrorType = "FieldValueInvalid"
	// ErrorTypeNotSupported is used to report unknown values for enumerated
	// fields (e.g. a list of valid values).  See NotSupported().
	ErrorTypeNotSupported ErrorType = "FieldValueNotSupported"
	// ErrorTypeForbidden is used to report valid (as per formatting rules)
	// values which would be accepted under some conditions, but which are not
	// permitted by the current conditions (such as security policy).  See
	// Forbidden().
	ErrorTypeForbidden ErrorType = "FieldValueForbidden"
	// ErrorTypeTooLong is used to report that the given value is too long.
	// This is similar to ErrorTypeInvalid, but the error will not include the
	// too-long value.  See TooLong().
	ErrorTypeTooLong ErrorType = "FieldValueTooLong"
	// ErrorTypeTooMany is used to report "too many". This is used to
	// report that a given list has too many items. This is similar to FieldValueTooLong,
	// but the error indicates quantity instead of length.
	ErrorTypeTooMany ErrorType = "FieldValueTooMany"
	// ErrorTypeInternal is used to report other errors that are not related
	// to user input.  See InternalError().
	ErrorTypeInternal ErrorType = "InternalError"
)

// String converts a ErrorType into its corresponding canonical error message.
func (t ErrorType) String() string {
	switch t {
	case ErrorTypeNotFound:
		return "Not found"
	case ErrorTypeRequired:
		return "Required value"
	case ErrorTypeDuplicate:
		return "Duplicate value"
	case ErrorTypeInvalid:
		return "Invalid value"
	case ErrorTypeNotSupported:
		return "Unsupported value"
	case ErrorTypeForbidden:
		return "Forbidden"
	case ErrorTypeTooLong:
		return "Too long"
	case ErrorTypeTooMany:
		return "Too many"
	case ErrorTypeInternal:
		return "Internal error"
	default:
		panic(fmt.Sprintf("unrecognized validation error: %q", string(t)))
	}
}

// NotFound returns a *Error indicating "value not found".  This is
// used to report failure to find a requested value (e.g. looking up an ID).
func NotFound(field *Path, value interface{}) *Error {
	return &Error{ErrorTypeNotFound, field.String(), value, ""}
}

// Required returns a *Error indicating "value required".  This is used
// to report required values that are not provided (e.g. empty strings, null
// values, or empty arrays).
func Required(field *Path, detail string) *Error {
	return &Error{ErrorTypeRequired, field.String(), "", detail}
}

// Duplicate returns a *Error indicating "duplicate value".  This is
// used to report collisions of values that must be unique (e.g. names or IDs).
func Duplicate(field *Path, value interface{}) *Error {
	return &Error{ErrorTypeDuplicate, field.String(), value, ""}
}

// Invalid returns a *Error indicating "invalid value".  This is used
// to report malformed values (e.g. failed regex match, too long, out of bounds).
func Invalid(field *Path, value interface{}, detail string) *Error {
	return &Error{ErrorTypeInvalid, field.String(), value, detail}
}

// NotSupported returns a *Error indicating "unsupported value".
// This is used to report unknown values for enumerated fields (e.g. a list of
// valid values).
func NotSupported(field *Path, value interface{}, validValues []string) *Error {
	detail := ""
	if len(validValues) > 0 {
		quotedValues := make([]string, len(validValues))
		for i, v := range validValues {
			quotedValues[i] = strconv.Quote(v)
		}
		detail = "supported values: " + strings.Join(quotedValues, ", ")
	}
	return &Error{ErrorTypeNotSupported, field.String(), value, detail}
}

// Forbidden returns a *Error indicating "forbidden".  This is used to
// report valid (as per formatting rules) values which would be accepted under
// some conditions, but which are not permitted by current conditions (e.g.
// security policy).
func Forbidden(field *Path, detail string) *Error {
	return &Error{ErrorTypeForbidden, field.String(), "", detail}
}

// TooLong returns a *Error indicating "too long".  This is used to
// report that the given value is too long.  This is similar to
// Invalid, but the returned error will not include the too-long
// value.
func TooLong(field *Path, value interface{}, maxLength int) *Error {
	return &Error{ErrorTypeTooLong, field.String(), value, fmt.Sprintf("must have at most %d bytes", maxLength)}
}

// TooMany returns a *Error indicating "too many". This is used to
// report that a given list has too many items. This is similar to TooLong,
// but the returned error indicates quantity instead of length.
func TooMany(field *Path, actualQuantity, maxQuantity int) *Error {
	return &Error{ErrorTypeTooMany, field.String(), actualQuantity, fmt.Sprintf("must have at most %d items", maxQuantity)}
}

// InternalError returns a *Error indicating "internal error".  This is used
// to signal that an error was found that was not directly related to user
// input.  The err argument must be non-nil.
func InternalError(field *Path, err error) *Error {
	return &Error{ErrorTypeInternal, field.String(), nil, err.Error()}
}

// ErrorList holds a set of Errors.  It is plausible that we might one day have
// non-field errors in this same umbrella package, but for now we don't, so
// we can keep it simple and leave ErrorList here.
type ErrorList []*Error

// NewErrorTypeMatcher returns an errors.Matcher that returns true
// if the provided error is a Error and has the provided ErrorType.
func NewErrorTypeMatcher(t ErrorType) utilerrors.Matcher {
	return func(err error) bool {
		if e, ok := err.(*Error); ok {
			return e.Type == t
		}
		return false
	}
}

// ToAggregate converts the ErrorList into an errors.Aggregate.
func (list ErrorList) ToAggregate() utilerrors.Aggregate {
	errs := make([]error, 0, len(list))
	errorMsgs := sets.NewString()
	for _, err := range list {
		msg := fmt.Sprintf("%v", err)
		if errorMsgs.Has(msg) {
			continue
		}
		errorMsgs.Insert(msg)
		errs = append(errs, err)
	}
	return utilerrors.NewAggregate(errs)
}

func fromAggregate(agg utilerrors.Aggregate) ErrorList {
	errs := agg.Errors()
	list := make(ErrorList, len(errs))
	for i := range errs {
		list[i] = errs[i].(*Error)
	}
	return list
}

// Filter removes items from the ErrorList that match the provided fns.
func (list ErrorList) Filter(fns ...utilerrors.Matcher) ErrorList {
	err := utilerrors.FilterOut(list.ToAggregate(), fns...)
	if err == nil {
		return nil
	}
	// FilterOut takes an Aggregate and returns an Aggregate
	return fromAggregate(err.(utilerrors.Aggregate))
}
<commit_msg>apierrors: optimize ToAggregate() for zero-length lists<commit_after>/*
Copyright 2014 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 field

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"

	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	"k8s.io/apimachinery/pkg/util/sets"
)

// Error is an implementation of the 'error' interface, which represents a
// field-level validation error.
type Error struct {
	Type     ErrorType
	Field    string
	BadValue interface{}
	Detail   string
}

var _ error = &Error{}

// Error implements the error interface.
func (v *Error) Error() string {
	return fmt.Sprintf("%s: %s", v.Field, v.ErrorBody())
}

// ErrorBody returns the error message without the field name.  This is useful
// for building nice-looking higher-level error reporting.
func (v *Error) ErrorBody() string {
	var s string
	switch v.Type {
	case ErrorTypeRequired, ErrorTypeForbidden, ErrorTypeTooLong, ErrorTypeInternal:
		s = v.Type.String()
	default:
		value := v.BadValue
		valueType := reflect.TypeOf(value)
		if value == nil || valueType == nil {
			value = "null"
		} else if valueType.Kind() == reflect.Ptr {
			if reflectValue := reflect.ValueOf(value); reflectValue.IsNil() {
				value = "null"
			} else {
				value = reflectValue.Elem().Interface()
			}
		}
		switch t := value.(type) {
		case int64, int32, float64, float32, bool:
			// use simple printer for simple types
			s = fmt.Sprintf("%s: %v", v.Type, value)
		case string:
			s = fmt.Sprintf("%s: %q", v.Type, t)
		case fmt.Stringer:
			// anything that defines String() is better than raw struct
			s = fmt.Sprintf("%s: %s", v.Type, t.String())
		default:
			// fallback to raw struct
			// TODO: internal types have panic guards against json.Marshalling to prevent
			// accidental use of internal types in external serialized form.  For now, use
			// %#v, although it would be better to show a more expressive output in the future
			s = fmt.Sprintf("%s: %#v", v.Type, value)
		}
	}
	if len(v.Detail) != 0 {
		s += fmt.Sprintf(": %s", v.Detail)
	}
	return s
}

// ErrorType is a machine readable value providing more detail about why
// a field is invalid.  These values are expected to match 1-1 with
// CauseType in api/types.go.
type ErrorType string

// TODO: These values are duplicated in api/types.go, but there's a circular dep.  Fix it.
const (
	// ErrorTypeNotFound is used to report failure to find a requested value
	// (e.g. looking up an ID).  See NotFound().
	ErrorTypeNotFound ErrorType = "FieldValueNotFound"
	// ErrorTypeRequired is used to report required values that are not
	// provided (e.g. empty strings, null values, or empty arrays).  See
	// Required().
	ErrorTypeRequired ErrorType = "FieldValueRequired"
	// ErrorTypeDuplicate is used to report collisions of values that must be
	// unique (e.g. unique IDs).  See Duplicate().
	ErrorTypeDuplicate ErrorType = "FieldValueDuplicate"
	// ErrorTypeInvalid is used to report malformed values (e.g. failed regex
	// match, too long, out of bounds).  See Invalid().
	ErrorTypeInvalid ErrorType = "FieldValueInvalid"
	// ErrorTypeNotSupported is used to report unknown values for enumerated
	// fields (e.g. a list of valid values).  See NotSupported().
	ErrorTypeNotSupported ErrorType = "FieldValueNotSupported"
	// ErrorTypeForbidden is used to report valid (as per formatting rules)
	// values which would be accepted under some conditions, but which are not
	// permitted by the current conditions (such as security policy).  See
	// Forbidden().
	ErrorTypeForbidden ErrorType = "FieldValueForbidden"
	// ErrorTypeTooLong is used to report that the given value is too long.
	// This is similar to ErrorTypeInvalid, but the error will not include the
	// too-long value.  See TooLong().
	ErrorTypeTooLong ErrorType = "FieldValueTooLong"
	// ErrorTypeTooMany is used to report "too many". This is used to
	// report that a given list has too many items. This is similar to FieldValueTooLong,
	// but the error indicates quantity instead of length.
	ErrorTypeTooMany ErrorType = "FieldValueTooMany"
	// ErrorTypeInternal is used to report other errors that are not related
	// to user input.  See InternalError().
	ErrorTypeInternal ErrorType = "InternalError"
)

// String converts a ErrorType into its corresponding canonical error message.
func (t ErrorType) String() string {
	switch t {
	case ErrorTypeNotFound:
		return "Not found"
	case ErrorTypeRequired:
		return "Required value"
	case ErrorTypeDuplicate:
		return "Duplicate value"
	case ErrorTypeInvalid:
		return "Invalid value"
	case ErrorTypeNotSupported:
		return "Unsupported value"
	case ErrorTypeForbidden:
		return "Forbidden"
	case ErrorTypeTooLong:
		return "Too long"
	case ErrorTypeTooMany:
		return "Too many"
	case ErrorTypeInternal:
		return "Internal error"
	default:
		panic(fmt.Sprintf("unrecognized validation error: %q", string(t)))
	}
}

// NotFound returns a *Error indicating "value not found".  This is
// used to report failure to find a requested value (e.g. looking up an ID).
func NotFound(field *Path, value interface{}) *Error {
	return &Error{ErrorTypeNotFound, field.String(), value, ""}
}

// Required returns a *Error indicating "value required".  This is used
// to report required values that are not provided (e.g. empty strings, null
// values, or empty arrays).
func Required(field *Path, detail string) *Error {
	return &Error{ErrorTypeRequired, field.String(), "", detail}
}

// Duplicate returns a *Error indicating "duplicate value".  This is
// used to report collisions of values that must be unique (e.g. names or IDs).
func Duplicate(field *Path, value interface{}) *Error {
	return &Error{ErrorTypeDuplicate, field.String(), value, ""}
}

// Invalid returns a *Error indicating "invalid value".  This is used
// to report malformed values (e.g. failed regex match, too long, out of bounds).
func Invalid(field *Path, value interface{}, detail string) *Error {
	return &Error{ErrorTypeInvalid, field.String(), value, detail}
}

// NotSupported returns a *Error indicating "unsupported value".
// This is used to report unknown values for enumerated fields (e.g. a list of
// valid values).
func NotSupported(field *Path, value interface{}, validValues []string) *Error {
	detail := ""
	if len(validValues) > 0 {
		quotedValues := make([]string, len(validValues))
		for i, v := range validValues {
			quotedValues[i] = strconv.Quote(v)
		}
		detail = "supported values: " + strings.Join(quotedValues, ", ")
	}
	return &Error{ErrorTypeNotSupported, field.String(), value, detail}
}

// Forbidden returns a *Error indicating "forbidden".  This is used to
// report valid (as per formatting rules) values which would be accepted under
// some conditions, but which are not permitted by current conditions (e.g.
// security policy).
func Forbidden(field *Path, detail string) *Error {
	return &Error{ErrorTypeForbidden, field.String(), "", detail}
}

// TooLong returns a *Error indicating "too long".  This is used to
// report that the given value is too long.  This is similar to
// Invalid, but the returned error will not include the too-long
// value.
func TooLong(field *Path, value interface{}, maxLength int) *Error {
	return &Error{ErrorTypeTooLong, field.String(), value, fmt.Sprintf("must have at most %d bytes", maxLength)}
}

// TooMany returns a *Error indicating "too many". This is used to
// report that a given list has too many items. This is similar to TooLong,
// but the returned error indicates quantity instead of length.
func TooMany(field *Path, actualQuantity, maxQuantity int) *Error {
	return &Error{ErrorTypeTooMany, field.String(), actualQuantity, fmt.Sprintf("must have at most %d items", maxQuantity)}
}

// InternalError returns a *Error indicating "internal error".  This is used
// to signal that an error was found that was not directly related to user
// input.  The err argument must be non-nil.
func InternalError(field *Path, err error) *Error {
	return &Error{ErrorTypeInternal, field.String(), nil, err.Error()}
}

// ErrorList holds a set of Errors.  It is plausible that we might one day have
// non-field errors in this same umbrella package, but for now we don't, so
// we can keep it simple and leave ErrorList here.
type ErrorList []*Error

// NewErrorTypeMatcher returns an errors.Matcher that returns true
// if the provided error is a Error and has the provided ErrorType.
func NewErrorTypeMatcher(t ErrorType) utilerrors.Matcher {
	return func(err error) bool {
		if e, ok := err.(*Error); ok {
			return e.Type == t
		}
		return false
	}
}

// ToAggregate converts the ErrorList into an errors.Aggregate.
func (list ErrorList) ToAggregate() utilerrors.Aggregate {
	if len(list) == 0 {
		return nil
	}
	errs := make([]error, 0, len(list))
	errorMsgs := sets.NewString()
	for _, err := range list {
		msg := fmt.Sprintf("%v", err)
		if errorMsgs.Has(msg) {
			continue
		}
		errorMsgs.Insert(msg)
		errs = append(errs, err)
	}
	return utilerrors.NewAggregate(errs)
}

func fromAggregate(agg utilerrors.Aggregate) ErrorList {
	errs := agg.Errors()
	list := make(ErrorList, len(errs))
	for i := range errs {
		list[i] = errs[i].(*Error)
	}
	return list
}

// Filter removes items from the ErrorList that match the provided fns.
func (list ErrorList) Filter(fns ...utilerrors.Matcher) ErrorList {
	err := utilerrors.FilterOut(list.ToAggregate(), fns...)
	if err == nil {
		return nil
	}
	// FilterOut takes an Aggregate and returns an Aggregate
	return fromAggregate(err.(utilerrors.Aggregate))
}
<|endoftext|>


<commit_before>package guestfs_test

import (
	"fmt"

	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"
	corev1 "k8s.io/api/core/v1"
	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes/fake"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"

	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/testing"

	"kubevirt.io/client-go/kubecli"

	"kubevirt.io/kubevirt/pkg/virtctl/guestfs"

	virtctlcmd "kubevirt.io/kubevirt/tests/clientcmd"
)

const (
	commandName   = "guestfs"
	pvcName       = "test-pvc"
	testNamespace = "default"
)

func fakeAttacherCreator(client *guestfs.K8sClient, p *corev1.Pod, command string) error {
	return nil
}

func fakeSetImage(virtClient kubecli.KubevirtClient) error {
	return nil
}

func fakeGetImageInfo(virtClient kubecli.KubevirtClient) (*kubecli.GuestfsInfo, error) {
	info := &kubecli.GuestfsInfo{
		Registry:    "someregistry.io/kubevirt",
		Tag:         "sha256:07c601d33793ee987g5417d755665572dc9a9680cea01dfb9bdbcc3ecf866720",
		Digest:      "89af657d3c226ac3083a0986e19efe70c9ccd7e7278137e9df24b9b430182aa7",
		ImagePrefix: "some-prefix-",
	}

	return info, nil
}

var _ = Describe("Guestfs shell", func() {
	var (
		kubeClient     *fake.Clientset
		kubevirtClient *kubecli.MockKubevirtClient
	)
	mode := v1.PersistentVolumeFilesystem
	pvc := &v1.PersistentVolumeClaim{
		ObjectMeta: metav1.ObjectMeta{
			Name:      pvcName,
			Namespace: testNamespace,
		},
		Spec: v1.PersistentVolumeClaimSpec{
			VolumeMode: &mode,
		},
	}
	fakeCreateClientPVC := func(config *rest.Config, virtClientConfig clientcmd.ClientConfig) (*guestfs.K8sClient, error) {
		kubeClient = fake.NewSimpleClientset(pvc)
		kubeClient.Fake.PrependReactor("get", "pods", func(action testing.Action) (bool, runtime.Object, error) {
			podRunning := &v1.Pod{
				ObjectMeta: metav1.ObjectMeta{
					Name:      "libguestfs-tools",
					Namespace: testNamespace,
				},
				Status: v1.PodStatus{
					Phase: v1.PodRunning,
					ContainerStatuses: []v1.ContainerStatus{
						{
							Name: "virt",
							State: v1.ContainerState{
								Running: &v1.ContainerStateRunning{
									StartedAt: metav1.Time{},
								},
							},
						},
					},
				},
			}
			return true, podRunning, nil
		})
		return &guestfs.K8sClient{Client: kubeClient, VirtClient: kubevirtClient}, nil
	}
	fakeCreateClientPVCinUse := func(config *rest.Config, virtClientConfig clientcmd.ClientConfig) (*guestfs.K8sClient, error) {
		otherPod := &v1.Pod{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "test-pod",
				Namespace: testNamespace,
			},
			Spec: v1.PodSpec{
				Volumes: []v1.Volume{
					{
						Name: "volume-test",
						VolumeSource: v1.VolumeSource{
							PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
								ClaimName: pvcName,
							},
						},
					},
				},
			},
		}

		kubeClient = fake.NewSimpleClientset(pvc, otherPod)
		return &guestfs.K8sClient{Client: kubeClient, VirtClient: kubevirtClient}, nil
	}
	fakeCreateClient := func(config *rest.Config, virtClientConfig clientcmd.ClientConfig) (*guestfs.K8sClient, error) {
		kubeClient = fake.NewSimpleClientset()
		return &guestfs.K8sClient{Client: kubeClient, VirtClient: kubevirtClient}, nil
	}

	Context("attach to PVC", func() {
		BeforeEach(func() {
			guestfs.SetImageSetFunc(fakeSetImage)
			guestfs.SetAttacher(fakeAttacherCreator)
		})

		AfterEach(func() {
			guestfs.SetDefaultImageSet()
			guestfs.SetDefaulAttacher()
		})

		It("Succesfully attach to PVC", func() {
			guestfs.SetClient(fakeCreateClientPVC)
			cmd := virtctlcmd.NewRepeatableVirtctlCommand(commandName, pvcName)
			Expect(cmd()).To(Succeed())
		})

		It("PVC in use", func() {
			guestfs.SetClient(fakeCreateClientPVCinUse)
			cmd := virtctlcmd.NewRepeatableVirtctlCommand(commandName, pvcName)
			err := cmd()
			Expect(err).To(HaveOccurred())
			Expect(err.Error()).Should(Equal(fmt.Sprintf("PVC %s is used by another pod", pvcName)))
		})

		It("PVC doesn't exist", func() {
			guestfs.SetClient(fakeCreateClient)
			cmd := virtctlcmd.NewRepeatableVirtctlCommand(commandName, pvcName)
			err := cmd()
			Expect(err).To(HaveOccurred())
			Expect(err.Error()).Should(Equal(fmt.Sprintf("The PVC %s doesn't exist", pvcName)))
		})

		It("UID cannot be used with root", func() {
			guestfs.SetClient(fakeCreateClientPVC)
			cmd := virtctlcmd.NewRepeatableVirtctlCommand(commandName, pvcName, "--root=true", "--uid=1001")
			err := cmd()
			Expect(err).To(HaveOccurred())
			Expect(err.Error()).Should(Equal(fmt.Sprintf("cannot set uid if root is true")))
		})
	})

	Context("URL authenticity", func() {
		BeforeEach(func() {
			guestfs.SetImageInfoGetFunc(fakeGetImageInfo)
		})

		AfterEach(func() {
			guestfs.SetDefaultImageInfoGetFunc()
		})

		It("Image prefix from kubevirt config not discarded", func() {
			guestfs.ImageSetFunc(kubevirtClient)
			Expect(guestfs.ExportedImage).To(Equal("someregistry.io/kubevirt/some-prefix-libguestfs-tools@89af657d3c226ac3083a0986e19efe70c9ccd7e7278137e9df24b9b430182aa7"))
		})
	})
})
<commit_msg>add unit test for gid<commit_after>package guestfs_test

import (
	"fmt"

	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"
	corev1 "k8s.io/api/core/v1"
	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes/fake"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"

	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/testing"

	"kubevirt.io/client-go/kubecli"

	"kubevirt.io/kubevirt/pkg/virtctl/guestfs"

	virtctlcmd "kubevirt.io/kubevirt/tests/clientcmd"
)

const (
	commandName   = "guestfs"
	pvcName       = "test-pvc"
	testNamespace = "default"
)

func fakeAttacherCreator(client *guestfs.K8sClient, p *corev1.Pod, command string) error {
	return nil
}

func fakeSetImage(virtClient kubecli.KubevirtClient) error {
	return nil
}

func fakeGetImageInfo(virtClient kubecli.KubevirtClient) (*kubecli.GuestfsInfo, error) {
	info := &kubecli.GuestfsInfo{
		Registry:    "someregistry.io/kubevirt",
		Tag:         "sha256:07c601d33793ee987g5417d755665572dc9a9680cea01dfb9bdbcc3ecf866720",
		Digest:      "89af657d3c226ac3083a0986e19efe70c9ccd7e7278137e9df24b9b430182aa7",
		ImagePrefix: "some-prefix-",
	}

	return info, nil
}

var _ = Describe("Guestfs shell", func() {
	var (
		kubeClient     *fake.Clientset
		kubevirtClient *kubecli.MockKubevirtClient
	)
	mode := v1.PersistentVolumeFilesystem
	pvc := &v1.PersistentVolumeClaim{
		ObjectMeta: metav1.ObjectMeta{
			Name:      pvcName,
			Namespace: testNamespace,
		},
		Spec: v1.PersistentVolumeClaimSpec{
			VolumeMode: &mode,
		},
	}
	fakeCreateClientPVC := func(config *rest.Config, virtClientConfig clientcmd.ClientConfig) (*guestfs.K8sClient, error) {
		kubeClient = fake.NewSimpleClientset(pvc)
		kubeClient.Fake.PrependReactor("get", "pods", func(action testing.Action) (bool, runtime.Object, error) {
			podRunning := &v1.Pod{
				ObjectMeta: metav1.ObjectMeta{
					Name:      "libguestfs-tools",
					Namespace: testNamespace,
				},
				Status: v1.PodStatus{
					Phase: v1.PodRunning,
					ContainerStatuses: []v1.ContainerStatus{
						{
							Name: "virt",
							State: v1.ContainerState{
								Running: &v1.ContainerStateRunning{
									StartedAt: metav1.Time{},
								},
							},
						},
					},
				},
			}
			return true, podRunning, nil
		})
		return &guestfs.K8sClient{Client: kubeClient, VirtClient: kubevirtClient}, nil
	}
	fakeCreateClientPVCinUse := func(config *rest.Config, virtClientConfig clientcmd.ClientConfig) (*guestfs.K8sClient, error) {
		otherPod := &v1.Pod{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "test-pod",
				Namespace: testNamespace,
			},
			Spec: v1.PodSpec{
				Volumes: []v1.Volume{
					{
						Name: "volume-test",
						VolumeSource: v1.VolumeSource{
							PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
								ClaimName: pvcName,
							},
						},
					},
				},
			},
		}

		kubeClient = fake.NewSimpleClientset(pvc, otherPod)
		return &guestfs.K8sClient{Client: kubeClient, VirtClient: kubevirtClient}, nil
	}
	fakeCreateClient := func(config *rest.Config, virtClientConfig clientcmd.ClientConfig) (*guestfs.K8sClient, error) {
		kubeClient = fake.NewSimpleClientset()
		return &guestfs.K8sClient{Client: kubeClient, VirtClient: kubevirtClient}, nil
	}

	Context("attach to PVC", func() {
		BeforeEach(func() {
			guestfs.SetImageSetFunc(fakeSetImage)
			guestfs.SetAttacher(fakeAttacherCreator)
		})

		AfterEach(func() {
			guestfs.SetDefaultImageSet()
			guestfs.SetDefaulAttacher()
		})

		It("Succesfully attach to PVC", func() {
			guestfs.SetClient(fakeCreateClientPVC)
			cmd := virtctlcmd.NewRepeatableVirtctlCommand(commandName, pvcName)
			Expect(cmd()).To(Succeed())
		})

		It("PVC in use", func() {
			guestfs.SetClient(fakeCreateClientPVCinUse)
			cmd := virtctlcmd.NewRepeatableVirtctlCommand(commandName, pvcName)
			err := cmd()
			Expect(err).To(HaveOccurred())
			Expect(err.Error()).Should(Equal(fmt.Sprintf("PVC %s is used by another pod", pvcName)))
		})

		It("PVC doesn't exist", func() {
			guestfs.SetClient(fakeCreateClient)
			cmd := virtctlcmd.NewRepeatableVirtctlCommand(commandName, pvcName)
			err := cmd()
			Expect(err).To(HaveOccurred())
			Expect(err.Error()).Should(Equal(fmt.Sprintf("The PVC %s doesn't exist", pvcName)))
		})

		It("UID cannot be used with root", func() {
			guestfs.SetClient(fakeCreateClientPVC)
			cmd := virtctlcmd.NewRepeatableVirtctlCommand(commandName, pvcName, "--root=true", "--uid=1001")
			err := cmd()
			Expect(err).To(HaveOccurred())
			Expect(err.Error()).Should(Equal(fmt.Sprintf("cannot set uid if root is true")))
		})
		It("GID can be use only together with the uid flag", func() {
			guestfs.SetClient(fakeCreateClientPVC)
			cmd := virtctlcmd.NewRepeatableVirtctlCommand(commandName, pvcName, "--gid=1001")
			err := cmd()
			Expect(err).To(HaveOccurred())
			Expect(err.Error()).Should(Equal(fmt.Sprintf("gid requires the uid to be set")))
		})
	})

	Context("URL authenticity", func() {
		BeforeEach(func() {
			guestfs.SetImageInfoGetFunc(fakeGetImageInfo)
		})

		AfterEach(func() {
			guestfs.SetDefaultImageInfoGetFunc()
		})

		It("Image prefix from kubevirt config not discarded", func() {
			guestfs.ImageSetFunc(kubevirtClient)
			Expect(guestfs.ExportedImage).To(Equal("someregistry.io/kubevirt/some-prefix-libguestfs-tools@89af657d3c226ac3083a0986e19efe70c9ccd7e7278137e9df24b9b430182aa7"))
		})
	})
})
<|endoftext|>


<commit_before>// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.
// pprof封装.

package ghttp

import (
    "strings"
    "gitee.com/johng/gf/g/os/gview"
    "gitee.com/johng/gf/g/os/gproc"
    "sync"
    "gitee.com/johng/gf/g/os/gtime"
    "errors"
    "fmt"
    "gitee.com/johng/gf/g/container/gtype"
    "gitee.com/johng/gf/g/os/glog"
    "os"
    "gitee.com/johng/gf/g/encoding/gjson"
    "gitee.com/johng/gf/g/util/gconv"
    "time"
    "runtime"
)

const (
    gADMIN_ACTION_INTERVAL_LIMIT = 2000 // (毫秒)服务开启后允许执行管理操作的间隔限制
    gADMIN_ACTION_RESTARTING     = 1
    gADMIN_ACTION_SHUTINGDOWN    = 2
    gADMIN_ACTION_RELOAD_ENVKEY  = "gf.server.reload"
    gADMIN_ACTION_RESTART_ENVKEY = "gf.server.restart"
)

// 用于服务管理的对象
type utilAdmin struct {}

// (进程级别)用于Web Server管理操作的互斥锁，保证管理操作的原子性
var serverActionLocker sync.Mutex

// (进程级别)用于记录上一次操作的时间(毫秒)
var serverActionLastTime = gtype.NewInt64(gtime.Millisecond())

// 当前服务进程所处的互斥管理操作状态
var serverProcessStatus  = gtype.NewInt()

// 服务管理首页
func (p *utilAdmin) Index(r *Request) {
    data := map[string]interface{}{
        "uri" : strings.TrimRight(r.URL.Path, "/"),
    }
    buffer, _ := gview.ParseContent(`

                <title>gf ghttp admin</title>

restart

shutdown

    `, data)
    r.Response.Write(buffer)
}

// 服务重启
func (p *utilAdmin) Restart(r *Request) {
    if err := r.Server.Restart(); err == nil {
        r.Response.Write("server restarted")
    } else {
        r.Response.Write(err.Error())
    }
}

// 服务关闭
func (p *utilAdmin) Shutdown(r *Request) {
    r.Server.Shutdown()
    if err := r.Server.Shutdown(); err == nil {
        r.Response.Write("server shutdown")
    } else {
        r.Response.Write(err.Error())
    }
}

// 开启服务管理支持
func (s *Server) EnableAdmin(pattern...string) {
    p := "/debug/admin"
    if len(pattern) > 0 {
        p = pattern[0]
    }
    s.BindObject(p, &utilAdmin{})
}

// 重启Web Server，参数支持自定义重启的可执行文件路径，不传递时默认和原有可执行文件路径一致。
// 针对*niux系统: 平滑重启
// 针对windows : 完整重启
func (s *Server) Restart(newExeFilePath...string) error {
    serverActionLocker.Lock()
    defer serverActionLocker.Unlock()
    if err := s.checkActionStatus(); err != nil {
        return err
    }
    if err := s.checkActionFrequence(); err != nil {
        return err
    }
    restartWebServers(newExeFilePath...)
    return nil
}

// 关闭Web Server
func (s *Server) Shutdown() error {
    serverActionLocker.Lock()
    defer serverActionLocker.Unlock()
    if err := s.checkActionStatus(); err != nil {
        return err
    }
    if err := s.checkActionFrequence(); err != nil {
        return err
    }
    shutdownWebServers()
    return nil
}

// 检测当前操作的频繁度
func (s *Server) checkActionFrequence() error {
    interval := gtime.Millisecond() - serverActionLastTime.Val()
    if interval < gADMIN_ACTION_INTERVAL_LIMIT {
        return errors.New(fmt.Sprintf("too frequent action, please retry in %d ms", gADMIN_ACTION_INTERVAL_LIMIT - interval))
    }
    serverActionLastTime.Set(gtime.Millisecond())
    return nil
}

// 检查当前服务进程的状态
func (s *Server) checkActionStatus() error {
    status := serverProcessStatus.Val()
    if status > 0 {
        switch status {
            case gADMIN_ACTION_RESTARTING:  return errors.New("server is restarting")
            case gADMIN_ACTION_SHUTINGDOWN: return errors.New("server is shutting down")
        }
    }
    return nil
}

// 平滑重启：创建一个子进程，通过环境变量传参
func forkReloadProcess(newExeFilePath...string) {
    path := os.Args[0]
    if len(newExeFilePath) > 0 {
        path = newExeFilePath[0]
    }
    p   := procManager.NewProcess(path, os.Args, os.Environ())
    // 创建新的服务进程，子进程自动从父进程复制文件描述来监听同样的端口
    sfm := getServerFdMap()
    // 将sfm中的fd按照子进程创建时的文件描述符顺序进行整理，以便子进程获取到正确的fd
    for name, m := range sfm {
        for fdk, fdv := range m {
            if len(fdv) > 0 {
                s := ""
                for _, item := range strings.Split(fdv, ",") {
                    array := strings.Split(item, "#")
                    fd    := uintptr(gconv.Uint(array[1]))
                    if fd > 0 {
                        s += fmt.Sprintf("%s#%d,", array[0], 3 + len(p.ExtraFiles))
                        p.ExtraFiles = append(p.ExtraFiles, os.NewFile(fd, ""))
                    } else {
                        s += fmt.Sprintf("%s#%d,", array[0], 0)
                    }
                }
                sfm[name][fdk] = strings.TrimRight(s, ",")
            }
        }
    }
    buffer, _ := gjson.Encode(sfm)
    p.Env = append(p.Env, gADMIN_ACTION_RELOAD_ENVKEY + "=" + string(buffer))
    if _, err := p.Start(); err != nil {
        glog.Errorfln("%d: fork process failed, error:%s, %s", gproc.Pid(), err.Error(), string(buffer))
    }
}

// 完整重启：创建一个新的子进程
func forkRestartProcess(newExeFilePath...string) {
    path := os.Args[0]
    if len(newExeFilePath) > 0 {
        path = newExeFilePath[0]
    }
    // 去掉平滑重启的环境变量参数
    os.Unsetenv(gADMIN_ACTION_RELOAD_ENVKEY)
    env := os.Environ()
    env  = append(env, gADMIN_ACTION_RESTART_ENVKEY + "=1")
    p := procManager.NewProcess(path, os.Args, env)
    if _, err := p.Start(); err != nil {
        glog.Errorfln("%d: fork process failed, error:%s", gproc.Pid(), err.Error())
    }
}

// 获取所有Web Server的文件描述符map
func getServerFdMap() map[string]listenerFdMap {
    sfm := make(map[string]listenerFdMap)
    serverMapping.RLockFunc(func(m map[string]interface{}) {
        for k, v := range m {
            sfm[k] = v.(*Server).getListenerFdMap()
        }
    })
    return sfm
}

// 二进制转换为FdMap
func bufferToServerFdMap(buffer []byte) map[string]listenerFdMap {
    sfm := make(map[string]listenerFdMap)
    if len(buffer) > 0 {
        j, _ := gjson.LoadContent(buffer, "json")
        for k, _ := range j.ToMap() {
            m := make(map[string]string)
            for k, v := range j.GetMap(k) {
                m[k] = gconv.String(v)
            }
            sfm[k] = m
        }
    }
    return sfm
}

// Web Server重启
func restartWebServers(newExeFilePath...string) {
    serverProcessStatus.Set(gADMIN_ACTION_RESTARTING)
    glog.Printfln("%d: server restarting", gproc.Pid())
    if runtime.GOOS == "windows" {
        // 异步1秒后再执行重启，目的是让接口能够正确返回结果，否则接口会报错(因为web server关闭了)
        gtime.SetTimeout(time.Second, func() {
            forcedlyCloseWebServers()
            forkRestartProcess(newExeFilePath...)
        })
    } else {
        forkReloadProcess(newExeFilePath...)
        go gracefulShutdownWebServers()
        doneChan <- struct{}{}
    }
}

// Web Server关闭服务
func shutdownWebServers() {
    serverProcessStatus.Set(gADMIN_ACTION_SHUTINGDOWN)
    glog.Printfln("%d: server shutting down", gproc.Pid())
    // 异步1秒后再执行重启，目的是让接口能够正确返回结果，否则接口会报错(因为web server关闭了)
    gtime.SetTimeout(time.Second, func() {
        forcedlyCloseWebServers()
        doneChan <- struct{}{}
    })
}

// 关优雅闭进程所有端口的Web Server服务
// 注意，只是关闭Web Server服务，并不是退出进程
func gracefulShutdownWebServers() {
    serverMapping.RLockFunc(func(m map[string]interface{}) {
        for _, v := range m {
            for _, s := range v.(*Server).servers {
                s.shutdown()
            }
        }
    })
}

// 强制关闭进程所有端口的Web Server服务
// 注意，只是关闭Web Server服务，并不是退出进程
func forcedlyCloseWebServers() {
    serverMapping.RLockFunc(func(m map[string]interface{}) {
        for _, v := range m {
            for _, s := range v.(*Server).servers {
                s.close()
            }
        }
    })
}<commit_msg>ghttp.Server.EnableAdmin页面Restart接口支持GET参数newExeFilePath支持<commit_after>// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.
// pprof封装.

package ghttp

import (
    "strings"
    "gitee.com/johng/gf/g/os/gview"
    "gitee.com/johng/gf/g/os/gproc"
    "sync"
    "gitee.com/johng/gf/g/os/gtime"
    "errors"
    "fmt"
    "gitee.com/johng/gf/g/container/gtype"
    "gitee.com/johng/gf/g/os/glog"
    "os"
    "gitee.com/johng/gf/g/encoding/gjson"
    "gitee.com/johng/gf/g/util/gconv"
    "time"
    "runtime"
)

const (
    gADMIN_ACTION_INTERVAL_LIMIT = 2000 // (毫秒)服务开启后允许执行管理操作的间隔限制
    gADMIN_ACTION_RESTARTING     = 1
    gADMIN_ACTION_SHUTINGDOWN    = 2
    gADMIN_ACTION_RELOAD_ENVKEY  = "gf.server.reload"
    gADMIN_ACTION_RESTART_ENVKEY = "gf.server.restart"
)

// 用于服务管理的对象
type utilAdmin struct {}

// (进程级别)用于Web Server管理操作的互斥锁，保证管理操作的原子性
var serverActionLocker sync.Mutex

// (进程级别)用于记录上一次操作的时间(毫秒)
var serverActionLastTime = gtype.NewInt64(gtime.Millisecond())

// 当前服务进程所处的互斥管理操作状态
var serverProcessStatus  = gtype.NewInt()

// 服务管理首页
func (p *utilAdmin) Index(r *Request) {
    data := map[string]interface{}{
        "uri" : strings.TrimRight(r.URL.Path, "/"),
    }
    buffer, _ := gview.ParseContent(`

                <title>gf ghttp admin</title>

restart

shutdown

    `, data)
    r.Response.Write(buffer)
}

// 服务重启
func (p *utilAdmin) Restart(r *Request) {
    var err error = nil
    path := r.GetQueryString("newExeFilePath")
    if len(path) > 0 {
        err = r.Server.Restart(path)
    } else {
        err = r.Server.Restart()
    }
    if err == nil {
        r.Response.Write("server restarted")
    } else {
        r.Response.Write(err.Error())
    }
}

// 服务关闭
func (p *utilAdmin) Shutdown(r *Request) {
    r.Server.Shutdown()
    if err := r.Server.Shutdown(); err == nil {
        r.Response.Write("server shutdown")
    } else {
        r.Response.Write(err.Error())
    }
}

// 开启服务管理支持
func (s *Server) EnableAdmin(pattern...string) {
    p := "/debug/admin"
    if len(pattern) > 0 {
        p = pattern[0]
    }
    s.BindObject(p, &utilAdmin{})
}

// 重启Web Server，参数支持自定义重启的可执行文件路径，不传递时默认和原有可执行文件路径一致。
// 针对*niux系统: 平滑重启
// 针对windows : 完整重启
func (s *Server) Restart(newExeFilePath...string) error {
    serverActionLocker.Lock()
    defer serverActionLocker.Unlock()
    if err := s.checkActionStatus(); err != nil {
        return err
    }
    if err := s.checkActionFrequence(); err != nil {
        return err
    }
    restartWebServers(newExeFilePath...)
    return nil
}

// 关闭Web Server
func (s *Server) Shutdown() error {
    serverActionLocker.Lock()
    defer serverActionLocker.Unlock()
    if err := s.checkActionStatus(); err != nil {
        return err
    }
    if err := s.checkActionFrequence(); err != nil {
        return err
    }
    shutdownWebServers()
    return nil
}

// 检测当前操作的频繁度
func (s *Server) checkActionFrequence() error {
    interval := gtime.Millisecond() - serverActionLastTime.Val()
    if interval < gADMIN_ACTION_INTERVAL_LIMIT {
        return errors.New(fmt.Sprintf("too frequent action, please retry in %d ms", gADMIN_ACTION_INTERVAL_LIMIT - interval))
    }
    serverActionLastTime.Set(gtime.Millisecond())
    return nil
}

// 检查当前服务进程的状态
func (s *Server) checkActionStatus() error {
    status := serverProcessStatus.Val()
    if status > 0 {
        switch status {
            case gADMIN_ACTION_RESTARTING:  return errors.New("server is restarting")
            case gADMIN_ACTION_SHUTINGDOWN: return errors.New("server is shutting down")
        }
    }
    return nil
}

// 平滑重启：创建一个子进程，通过环境变量传参
func forkReloadProcess(newExeFilePath...string) {
    path := os.Args[0]
    if len(newExeFilePath) > 0 {
        path = newExeFilePath[0]
    }
    p   := procManager.NewProcess(path, os.Args, os.Environ())
    // 创建新的服务进程，子进程自动从父进程复制文件描述来监听同样的端口
    sfm := getServerFdMap()
    // 将sfm中的fd按照子进程创建时的文件描述符顺序进行整理，以便子进程获取到正确的fd
    for name, m := range sfm {
        for fdk, fdv := range m {
            if len(fdv) > 0 {
                s := ""
                for _, item := range strings.Split(fdv, ",") {
                    array := strings.Split(item, "#")
                    fd    := uintptr(gconv.Uint(array[1]))
                    if fd > 0 {
                        s += fmt.Sprintf("%s#%d,", array[0], 3 + len(p.ExtraFiles))
                        p.ExtraFiles = append(p.ExtraFiles, os.NewFile(fd, ""))
                    } else {
                        s += fmt.Sprintf("%s#%d,", array[0], 0)
                    }
                }
                sfm[name][fdk] = strings.TrimRight(s, ",")
            }
        }
    }
    buffer, _ := gjson.Encode(sfm)
    p.Env = append(p.Env, gADMIN_ACTION_RELOAD_ENVKEY + "=" + string(buffer))
    if _, err := p.Start(); err != nil {
        glog.Errorfln("%d: fork process failed, error:%s, %s", gproc.Pid(), err.Error(), string(buffer))
    }
}

// 完整重启：创建一个新的子进程
func forkRestartProcess(newExeFilePath...string) {
    path := os.Args[0]
    if len(newExeFilePath) > 0 {
        path = newExeFilePath[0]
    }
    // 去掉平滑重启的环境变量参数
    os.Unsetenv(gADMIN_ACTION_RELOAD_ENVKEY)
    env := os.Environ()
    env  = append(env, gADMIN_ACTION_RESTART_ENVKEY + "=1")
    p := procManager.NewProcess(path, os.Args, env)
    if _, err := p.Start(); err != nil {
        glog.Errorfln("%d: fork process failed, error:%s", gproc.Pid(), err.Error())
    }
}

// 获取所有Web Server的文件描述符map
func getServerFdMap() map[string]listenerFdMap {
    sfm := make(map[string]listenerFdMap)
    serverMapping.RLockFunc(func(m map[string]interface{}) {
        for k, v := range m {
            sfm[k] = v.(*Server).getListenerFdMap()
        }
    })
    return sfm
}

// 二进制转换为FdMap
func bufferToServerFdMap(buffer []byte) map[string]listenerFdMap {
    sfm := make(map[string]listenerFdMap)
    if len(buffer) > 0 {
        j, _ := gjson.LoadContent(buffer, "json")
        for k, _ := range j.ToMap() {
            m := make(map[string]string)
            for k, v := range j.GetMap(k) {
                m[k] = gconv.String(v)
            }
            sfm[k] = m
        }
    }
    return sfm
}

// Web Server重启
func restartWebServers(newExeFilePath...string) {
    serverProcessStatus.Set(gADMIN_ACTION_RESTARTING)
    glog.Printfln("%d: server restarting", gproc.Pid())
    if runtime.GOOS == "windows" {
        // 异步1秒后再执行重启，目的是让接口能够正确返回结果，否则接口会报错(因为web server关闭了)
        gtime.SetTimeout(time.Second, func() {
            forcedlyCloseWebServers()
            forkRestartProcess(newExeFilePath...)
        })
    } else {
        forkReloadProcess(newExeFilePath...)
        go gracefulShutdownWebServers()
        doneChan <- struct{}{}
    }
}

// Web Server关闭服务
func shutdownWebServers() {
    serverProcessStatus.Set(gADMIN_ACTION_SHUTINGDOWN)
    glog.Printfln("%d: server shutting down", gproc.Pid())
    // 异步1秒后再执行重启，目的是让接口能够正确返回结果，否则接口会报错(因为web server关闭了)
    gtime.SetTimeout(time.Second, func() {
        forcedlyCloseWebServers()
        doneChan <- struct{}{}
    })
}

// 关优雅闭进程所有端口的Web Server服务
// 注意，只是关闭Web Server服务，并不是退出进程
func gracefulShutdownWebServers() {
    serverMapping.RLockFunc(func(m map[string]interface{}) {
        for _, v := range m {
            for _, s := range v.(*Server).servers {
                s.shutdown()
            }
        }
    })
}

// 强制关闭进程所有端口的Web Server服务
// 注意，只是关闭Web Server服务，并不是退出进程
func forcedlyCloseWebServers() {
    serverMapping.RLockFunc(func(m map[string]interface{}) {
        for _, v := range m {
            for _, s := range v.(*Server).servers {
                s.close()
            }
        }
    })
}<|endoftext|>


<commit_before>// Generic Interrupt Pins.

package generic

import (
	"errors"
	"fmt"
	"sync"
	"syscall"

	"github.com/kidoman/embd"
)

const (
	MaxGPIOInterrupt = 64
)

var ErrorPinAlreadyRegistered = errors.New("pin interrupt already registered")

type interrupt struct {
	pin            embd.DigitalPin
	initialTrigger bool
	handler        func(embd.DigitalPin)
}

func (i *interrupt) Signal() {
	if !i.initialTrigger {
		i.initialTrigger = true
		return
	}
	i.handler(i.pin)
}

type epollListener struct {
	mu                sync.Mutex // Guards the following.
	fd                int
	interruptablePins map[int]*interrupt
}

var epollListenerInstance *epollListener

func getEpollListenerInstance() *epollListener {
	if epollListenerInstance == nil {
		epollListenerInstance = initEpollListener()
	}
	return epollListenerInstance
}

func initEpollListener() *epollListener {
	fd, err := syscall.EpollCreate1(0)
	if err != nil {
		panic(fmt.Sprintf("Unable to create epoll: %v", err))
	}
	listener := &epollListener{fd: fd, interruptablePins: make(map[int]*interrupt)}

	go func() {
		var epollEvents [MaxGPIOInterrupt]syscall.EpollEvent

		for {
			n, err := syscall.EpollWait(listener.fd, epollEvents[:], -1)
			if err != nil {
				panic(fmt.Sprintf("EpollWait error: %v", err))
			}
			for i := 0; i < n; i++ {
				if irq, ok := listener.interruptablePins[int(epollEvents[i].Fd)]; ok {
					irq.Signal()
				}
			}
		}
	}()
	return listener
}

func registerInterrupt(pin *digitalPin, handler func(embd.DigitalPin)) error {
	l := getEpollListenerInstance()

	pinFd := int(pin.val.Fd())

	l.mu.Lock()
	defer l.mu.Unlock()

	if _, ok := l.interruptablePins[pinFd]; ok {
		return ErrorPinAlreadyRegistered
	}

	var event syscall.EpollEvent
	event.Events = syscall.EPOLLIN | (syscall.EPOLLET & 0xffffffff) | syscall.EPOLLPRI

	if err := syscall.SetNonblock(pinFd, true); err != nil {
		return err
	}

	event.Fd = int32(pinFd)

	if err := syscall.EpollCtl(l.fd, syscall.EPOLL_CTL_ADD, pinFd, &event); err != nil {
		return err
	}

	l.interruptablePins[pinFd] = &interrupt{pin: pin, handler: handler}

	return nil
}

func unregisterInterrupt(pin *digitalPin) error {
	l := getEpollListenerInstance()

	pinFd := int(pin.val.Fd())

	l.mu.Lock()
	defer l.mu.Unlock()

	if _, ok := l.interruptablePins[pinFd]; !ok {
		return nil
	}

	if err := syscall.EpollCtl(l.fd, syscall.EPOLL_CTL_DEL, pinFd, nil); err != nil {
		return err
	}

	if err := syscall.SetNonblock(pinFd, false); err != nil {
		return err
	}

	delete(l.interruptablePins, pinFd)
	return nil
}
<commit_msg>fix fatal error: concurrent map read and map write<commit_after>// Generic Interrupt Pins.

package generic

import (
	"errors"
	"fmt"
	"sync"
	"syscall"

	"github.com/kidoman/embd"
)

const (
	MaxGPIOInterrupt = 64
)

var ErrorPinAlreadyRegistered = errors.New("pin interrupt already registered")

type interrupt struct {
	pin            embd.DigitalPin
	initialTrigger bool
	handler        func(embd.DigitalPin)
}

func (i *interrupt) Signal() {
	if !i.initialTrigger {
		i.initialTrigger = true
		return
	}
	i.handler(i.pin)
}

type epollListener struct {
	mu                sync.Mutex // Guards the following.
	fd                int
	interruptablePins map[int]*interrupt
}

var epollListenerInstance *epollListener

func getEpollListenerInstance() *epollListener {
	if epollListenerInstance == nil {
		epollListenerInstance = initEpollListener()
	}
	return epollListenerInstance
}

func initEpollListener() *epollListener {
	fd, err := syscall.EpollCreate1(0)
	if err != nil {
		panic(fmt.Sprintf("Unable to create epoll: %v", err))
	}
	listener := &epollListener{fd: fd, interruptablePins: make(map[int]*interrupt)}

	go func() {
		var epollEvents [MaxGPIOInterrupt]syscall.EpollEvent

		for {
			n, err := syscall.EpollWait(listener.fd, epollEvents[:], -1)
			if err != nil {
				panic(fmt.Sprintf("EpollWait error: %v", err))
			}
			listener.mu.Lock()
			for i := 0; i < n; i++ {
				if irq, ok := listener.interruptablePins[int(epollEvents[i].Fd)]; ok {
					irq.Signal()
				}
			}
			listener.mu.Unlock()
		}
	}()
	return listener
}

func registerInterrupt(pin *digitalPin, handler func(embd.DigitalPin)) error {
	l := getEpollListenerInstance()

	pinFd := int(pin.val.Fd())

	l.mu.Lock()
	defer l.mu.Unlock()

	if _, ok := l.interruptablePins[pinFd]; ok {
		return ErrorPinAlreadyRegistered
	}

	var event syscall.EpollEvent
	event.Events = syscall.EPOLLIN | (syscall.EPOLLET & 0xffffffff) | syscall.EPOLLPRI

	if err := syscall.SetNonblock(pinFd, true); err != nil {
		return err
	}

	event.Fd = int32(pinFd)

	if err := syscall.EpollCtl(l.fd, syscall.EPOLL_CTL_ADD, pinFd, &event); err != nil {
		return err
	}

	l.interruptablePins[pinFd] = &interrupt{pin: pin, handler: handler}

	return nil
}

func unregisterInterrupt(pin *digitalPin) error {
	l := getEpollListenerInstance()

	pinFd := int(pin.val.Fd())

	l.mu.Lock()
	defer l.mu.Unlock()

	if _, ok := l.interruptablePins[pinFd]; !ok {
		return nil
	}

	if err := syscall.EpollCtl(l.fd, syscall.EPOLL_CTL_DEL, pinFd, nil); err != nil {
		return err
	}

	if err := syscall.SetNonblock(pinFd, false); err != nil {
		return err
	}

	delete(l.interruptablePins, pinFd)
	return nil
}
<|endoftext|>


<commit_before><commit_msg>Added tests for EstimatorServe<commit_after><|endoftext|>


<commit_before>/*
Copyright 2016 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 statefulset

import (
	"fmt"
	"strings"

	apierrors "k8s.io/apimachinery/pkg/api/errors"
	errorutils "k8s.io/apimachinery/pkg/util/errors"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/tools/record"
	"k8s.io/kubernetes/pkg/api/v1"
	apps "k8s.io/kubernetes/pkg/apis/apps/v1beta1"
	"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
	appslisters "k8s.io/kubernetes/pkg/client/listers/apps/v1beta1"
	corelisters "k8s.io/kubernetes/pkg/client/listers/core/v1"
	"k8s.io/kubernetes/pkg/client/retry"
)

// StatefulPodControlInterface defines the interface that StatefulSetController uses to create, update, and delete Pods,
// and to update the Status of a StatefulSet. It follows the design paradigms used for PodControl, but its
// implementation provides for PVC creation, ordered Pod creation, ordered Pod termination, and Pod identity enforcement.
// Like controller.PodControlInterface, it is implemented as an interface to provide for testing fakes.
type StatefulPodControlInterface interface {
	// CreateStatefulPod create a Pod in a StatefulSet. Any PVCs necessary for the Pod are created prior to creating
	// the Pod. If the returned error is nil the Pod and its PVCs have been created.
	CreateStatefulPod(set *apps.StatefulSet, pod *v1.Pod) error
	// UpdateStatefulPod Updates a Pod in a StatefulSet. If the Pod already has the correct identity and stable
	// storage this method is a no-op. If the Pod must be mutated to conform to the Set, it is mutated and updated.
	// pod is an in-out parameter, and any updates made to the pod are reflected as mutations to this parameter. If
	// the create is successful, the returned error is nil.
	UpdateStatefulPod(set *apps.StatefulSet, pod *v1.Pod) error
	// DeleteStatefulPod deletes a Pod in a StatefulSet. The pods PVCs are not deleted. If the delete is successful,
	// the returned error is nil.
	DeleteStatefulPod(set *apps.StatefulSet, pod *v1.Pod) error
	// UpdateStatefulSetStatus updates the status of a StatefulSet. set is an in-out parameter, and any
	// updates made to the set are made visible as mutations to the parameter. If the method is successful, the
	// returned error is nil, and set has its status updated.
	UpdateStatefulSetStatus(set *apps.StatefulSet, replicas int32, generation int64) error
}

func NewRealStatefulPodControl(
	client clientset.Interface,
	setLister appslisters.StatefulSetLister,
	podLister corelisters.PodLister,
	pvcLister corelisters.PersistentVolumeClaimLister,
	recorder record.EventRecorder,
) StatefulPodControlInterface {
	return &realStatefulPodControl{client, setLister, podLister, pvcLister, recorder}
}

// realStatefulPodControl implements StatefulPodControlInterface using a clientset.Interface to communicate with the
// API server. The struct is package private as the internal details are irrelevant to importing packages.
type realStatefulPodControl struct {
	client    clientset.Interface
	setLister appslisters.StatefulSetLister
	podLister corelisters.PodLister
	pvcLister corelisters.PersistentVolumeClaimLister
	recorder  record.EventRecorder
}

func (spc *realStatefulPodControl) CreateStatefulPod(set *apps.StatefulSet, pod *v1.Pod) error {
	// Create the Pod's PVCs prior to creating the Pod
	if err := spc.createPersistentVolumeClaims(set, pod); err != nil {
		spc.recordPodEvent("create", set, pod, err)
		return err
	}
	// If we created the PVCs attempt to create the Pod
	_, err := spc.client.Core().Pods(set.Namespace).Create(pod)
	// sink already exists errors
	if apierrors.IsAlreadyExists(err) {
		return err
	}
	spc.recordPodEvent("create", set, pod, err)
	return err
}

func (spc *realStatefulPodControl) UpdateStatefulPod(set *apps.StatefulSet, pod *v1.Pod) error {
	attemptedUpdate := false

	err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
		// assume the Pod is consistent
		consistent := true
		// if the Pod does not conform to its identity, update the identity and dirty the Pod
		if !identityMatches(set, pod) {
			updateIdentity(set, pod)
			consistent = false
		}
		// if the Pod does not conform to the StatefulSet's storage requirements, update the Pod's PVC's,
		// dirty the Pod, and create any missing PVCs
		if !storageMatches(set, pod) {
			updateStorage(set, pod)
			consistent = false
			if err := spc.createPersistentVolumeClaims(set, pod); err != nil {
				spc.recordPodEvent("update", set, pod, err)
				return err
			}
		}
		// if the Pod is not dirty do nothing
		if consistent {
			return nil
		}

		attemptedUpdate = true
		// commit the update, retrying on conflicts
		_, err := spc.client.Core().Pods(set.Namespace).Update(pod)
		if err == nil {
			return nil
		}
		updateErr := err

		if updated, err := spc.podLister.Pods(set.Namespace).Get(pod.Name); err == nil {
			// make a copy so we don't mutate the shared cache
			if copy, err := scheme.Scheme.DeepCopy(updated); err == nil {
				pod = copy.(*v1.Pod)
			} else {
				utilruntime.HandleError(fmt.Errorf("error copying updated Pod: %v", err))
			}
		} else {
			utilruntime.HandleError(fmt.Errorf("error getting updated Pod %s/%s from lister: %v", set.Namespace, pod.Name, err))
		}

		return updateErr
	})
	if attemptedUpdate {
		spc.recordPodEvent("update", set, pod, err)
	}
	return err
}

func (spc *realStatefulPodControl) DeleteStatefulPod(set *apps.StatefulSet, pod *v1.Pod) error {
	err := spc.client.Core().Pods(set.Namespace).Delete(pod.Name, nil)
	spc.recordPodEvent("delete", set, pod, err)
	return err
}

func (spc *realStatefulPodControl) UpdateStatefulSetStatus(set *apps.StatefulSet, replicas int32, generation int64) error {
	return retry.RetryOnConflict(retry.DefaultBackoff, func() error {
		set.Status.Replicas = replicas
		set.Status.ObservedGeneration = &generation
		_, err := spc.client.Apps().StatefulSets(set.Namespace).UpdateStatus(set)
		if err == nil {
			return nil
		}

		updateErr := err

		if updated, err := spc.setLister.StatefulSets(set.Namespace).Get(set.Name); err == nil {
			// make a copy so we don't mutate the shared cache
			if copy, err := scheme.Scheme.DeepCopy(updated); err == nil {
				set = copy.(*apps.StatefulSet)
			} else {
				utilruntime.HandleError(fmt.Errorf("error copying updated StatefulSet: %v", err))
			}
		} else {
			utilruntime.HandleError(fmt.Errorf("error getting updated StatefulSet %s/%s from lister: %v", set.Namespace, set.Name, err))
		}

		return updateErr
	})
}

// recordPodEvent records an event for verb applied to a Pod in a StatefulSet. If err is nil the generated event will
// have a reason of v1.EventTypeNormal. If err is not nil the generated event will have a reason of v1.EventTypeWarning.
func (spc *realStatefulPodControl) recordPodEvent(verb string, set *apps.StatefulSet, pod *v1.Pod, err error) {
	if err == nil {
		reason := fmt.Sprintf("Successful%s", strings.Title(verb))
		message := fmt.Sprintf("%s Pod %s in StatefulSet %s successful",
			strings.ToLower(verb), pod.Name, set.Name)
		spc.recorder.Event(set, v1.EventTypeNormal, reason, message)
	} else {
		reason := fmt.Sprintf("Failed%s", strings.Title(verb))
		message := fmt.Sprintf("%s Pod %s in StatefulSet %s failed error: %s",
			strings.ToLower(verb), pod.Name, set.Name, err)
		spc.recorder.Event(set, v1.EventTypeWarning, reason, message)
	}
}

// recordClaimEvent records an event for verb applied to the PersistentVolumeClaim of a Pod in a StatefulSet. If err is
// nil the generated event will have a reason of v1.EventTypeNormal. If err is not nil the generated event will have a
// reason of v1.EventTypeWarning.
func (spc *realStatefulPodControl) recordClaimEvent(verb string, set *apps.StatefulSet, pod *v1.Pod, claim *v1.PersistentVolumeClaim, err error) {
	if err == nil {
		reason := fmt.Sprintf("Successful%s", strings.Title(verb))
		message := fmt.Sprintf("%s Claim %s Pod %s in StatefulSet %s success",
			strings.ToLower(verb), claim.Name, pod.Name, set.Name)
		spc.recorder.Event(set, v1.EventTypeNormal, reason, message)
	} else {
		reason := fmt.Sprintf("Failed%s", strings.Title(verb))
		message := fmt.Sprintf("%s Claim %s for Pod %s in StatefulSet %s failed error: %s",
			strings.ToLower(verb), claim.Name, pod.Name, set.Name, err)
		spc.recorder.Event(set, v1.EventTypeWarning, reason, message)
	}
}

// createPersistentVolumeClaims creates all of the required PersistentVolumeClaims for pod, which mush be a member of
// set. If all of the claims for Pod are successfully created, the returned error is nil. If creation fails, this method
// may be called again until no error is returned, indicating the PersistentVolumeClaims for pod are consistent with
// set's Spec.
func (spc *realStatefulPodControl) createPersistentVolumeClaims(set *apps.StatefulSet, pod *v1.Pod) error {
	var errs []error
	for _, claim := range getPersistentVolumeClaims(set, pod) {
		_, err := spc.pvcLister.PersistentVolumeClaims(claim.Namespace).Get(claim.Name)
		switch {
		case apierrors.IsNotFound(err):
			_, err := spc.client.Core().PersistentVolumeClaims(claim.Namespace).Create(&claim)
			if err != nil {
				errs = append(errs, fmt.Errorf("Failed to create PVC %s: %s", claim.Name, err))
			}
			if err == nil || !apierrors.IsAlreadyExists(err) {
				spc.recordClaimEvent("create", set, pod, &claim, err)
			}
		case err != nil:
			errs = append(errs, fmt.Errorf("Failed to retrieve PVC %s: %s", claim.Name, err))
			spc.recordClaimEvent("create", set, pod, &claim, err)
		}
		// TODO: Check resource requirements and accessmodes, update if necessary
	}
	return errorutils.NewAggregate(errs)
}

var _ StatefulPodControlInterface = &realStatefulPodControl{}
<commit_msg>stateful_pod_control.go: format the code<commit_after>/*
Copyright 2016 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 statefulset

import (
	"fmt"
	"strings"

	apierrors "k8s.io/apimachinery/pkg/api/errors"
	errorutils "k8s.io/apimachinery/pkg/util/errors"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/tools/record"
	"k8s.io/kubernetes/pkg/api/v1"
	apps "k8s.io/kubernetes/pkg/apis/apps/v1beta1"
	"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
	appslisters "k8s.io/kubernetes/pkg/client/listers/apps/v1beta1"
	corelisters "k8s.io/kubernetes/pkg/client/listers/core/v1"
	"k8s.io/kubernetes/pkg/client/retry"
)

// StatefulPodControlInterface defines the interface that StatefulSetController uses to create, update, and delete Pods,
// and to update the Status of a StatefulSet. It follows the design paradigms used for PodControl, but its
// implementation provides for PVC creation, ordered Pod creation, ordered Pod termination, and Pod identity enforcement.
// Like controller.PodControlInterface, it is implemented as an interface to provide for testing fakes.
type StatefulPodControlInterface interface {
	// CreateStatefulPod create a Pod in a StatefulSet. Any PVCs necessary for the Pod are created prior to creating
	// the Pod. If the returned error is nil the Pod and its PVCs have been created.
	CreateStatefulPod(set *apps.StatefulSet, pod *v1.Pod) error
	// UpdateStatefulPod Updates a Pod in a StatefulSet. If the Pod already has the correct identity and stable
	// storage this method is a no-op. If the Pod must be mutated to conform to the Set, it is mutated and updated.
	// pod is an in-out parameter, and any updates made to the pod are reflected as mutations to this parameter. If
	// the create is successful, the returned error is nil.
	UpdateStatefulPod(set *apps.StatefulSet, pod *v1.Pod) error
	// DeleteStatefulPod deletes a Pod in a StatefulSet. The pods PVCs are not deleted. If the delete is successful,
	// the returned error is nil.
	DeleteStatefulPod(set *apps.StatefulSet, pod *v1.Pod) error
	// UpdateStatefulSetStatus updates the status of a StatefulSet. set is an in-out parameter, and any
	// updates made to the set are made visible as mutations to the parameter. If the method is successful, the
	// returned error is nil, and set has its status updated.
	UpdateStatefulSetStatus(set *apps.StatefulSet, replicas int32, generation int64) error
}

func NewRealStatefulPodControl(
	client clientset.Interface,
	setLister appslisters.StatefulSetLister,
	podLister corelisters.PodLister,
	pvcLister corelisters.PersistentVolumeClaimLister,
	recorder record.EventRecorder,
) StatefulPodControlInterface {
	return &realStatefulPodControl{client, setLister, podLister, pvcLister, recorder}
}

// realStatefulPodControl implements StatefulPodControlInterface using a clientset.Interface to communicate with the
// API server. The struct is package private as the internal details are irrelevant to importing packages.
type realStatefulPodControl struct {
	client    clientset.Interface
	setLister appslisters.StatefulSetLister
	podLister corelisters.PodLister
	pvcLister corelisters.PersistentVolumeClaimLister
	recorder  record.EventRecorder
}

func (spc *realStatefulPodControl) CreateStatefulPod(set *apps.StatefulSet, pod *v1.Pod) error {
	// Create the Pod's PVCs prior to creating the Pod
	if err := spc.createPersistentVolumeClaims(set, pod); err != nil {
		spc.recordPodEvent("create", set, pod, err)
		return err
	}
	// If we created the PVCs attempt to create the Pod
	_, err := spc.client.Core().Pods(set.Namespace).Create(pod)
	// sink already exists errors
	if apierrors.IsAlreadyExists(err) {
		return err
	}
	spc.recordPodEvent("create", set, pod, err)
	return err
}

func (spc *realStatefulPodControl) UpdateStatefulPod(set *apps.StatefulSet, pod *v1.Pod) error {
	attemptedUpdate := false

	err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
		// assume the Pod is consistent
		consistent := true
		// if the Pod does not conform to its identity, update the identity and dirty the Pod
		if !identityMatches(set, pod) {
			updateIdentity(set, pod)
			consistent = false
		}
		// if the Pod does not conform to the StatefulSet's storage requirements, update the Pod's PVC's,
		// dirty the Pod, and create any missing PVCs
		if !storageMatches(set, pod) {
			updateStorage(set, pod)
			consistent = false
			if err := spc.createPersistentVolumeClaims(set, pod); err != nil {
				spc.recordPodEvent("update", set, pod, err)
				return err
			}
		}
		// if the Pod is not dirty, do nothing
		if consistent {
			return nil
		}

		attemptedUpdate = true
		// commit the update, retrying on conflicts
		_, updateErr := spc.client.Core().Pods(set.Namespace).Update(pod)
		if updateErr == nil {
			return nil
		}

		if updated, err := spc.podLister.Pods(set.Namespace).Get(pod.Name); err == nil {
			// make a copy so we don't mutate the shared cache
			if copy, err := scheme.Scheme.DeepCopy(updated); err == nil {
				pod = copy.(*v1.Pod)
			} else {
				utilruntime.HandleError(fmt.Errorf("error copying updated Pod: %v", err))
			}
		} else {
			utilruntime.HandleError(fmt.Errorf("error getting updated Pod %s/%s from lister: %v", set.Namespace, pod.Name, err))
		}

		return updateErr
	})
	if attemptedUpdate {
		spc.recordPodEvent("update", set, pod, err)
	}
	return err
}

func (spc *realStatefulPodControl) DeleteStatefulPod(set *apps.StatefulSet, pod *v1.Pod) error {
	err := spc.client.Core().Pods(set.Namespace).Delete(pod.Name, nil)
	spc.recordPodEvent("delete", set, pod, err)
	return err
}

func (spc *realStatefulPodControl) UpdateStatefulSetStatus(set *apps.StatefulSet, replicas int32, generation int64) error {
	return retry.RetryOnConflict(retry.DefaultBackoff, func() error {
		set.Status.Replicas = replicas
		set.Status.ObservedGeneration = &generation
		_, updateErr := spc.client.Apps().StatefulSets(set.Namespace).UpdateStatus(set)
		if updateErr == nil {
			return nil
		}

		if updated, err := spc.setLister.StatefulSets(set.Namespace).Get(set.Name); err == nil {
			// make a copy so we don't mutate the shared cache
			if copy, err := scheme.Scheme.DeepCopy(updated); err == nil {
				set = copy.(*apps.StatefulSet)
			} else {
				utilruntime.HandleError(fmt.Errorf("error copying updated StatefulSet: %v", err))
			}
		} else {
			utilruntime.HandleError(fmt.Errorf("error getting updated StatefulSet %s/%s from lister: %v", set.Namespace, set.Name, err))
		}

		return updateErr
	})
}

// recordPodEvent records an event for verb applied to a Pod in a StatefulSet. If err is nil the generated event will
// have a reason of v1.EventTypeNormal. If err is not nil the generated event will have a reason of v1.EventTypeWarning.
func (spc *realStatefulPodControl) recordPodEvent(verb string, set *apps.StatefulSet, pod *v1.Pod, err error) {
	if err == nil {
		reason := fmt.Sprintf("Successful%s", strings.Title(verb))
		message := fmt.Sprintf("%s Pod %s in StatefulSet %s successful",
			strings.ToLower(verb), pod.Name, set.Name)
		spc.recorder.Event(set, v1.EventTypeNormal, reason, message)
	} else {
		reason := fmt.Sprintf("Failed%s", strings.Title(verb))
		message := fmt.Sprintf("%s Pod %s in StatefulSet %s failed error: %s",
			strings.ToLower(verb), pod.Name, set.Name, err)
		spc.recorder.Event(set, v1.EventTypeWarning, reason, message)
	}
}

// recordClaimEvent records an event for verb applied to the PersistentVolumeClaim of a Pod in a StatefulSet. If err is
// nil the generated event will have a reason of v1.EventTypeNormal. If err is not nil the generated event will have a
// reason of v1.EventTypeWarning.
func (spc *realStatefulPodControl) recordClaimEvent(verb string, set *apps.StatefulSet, pod *v1.Pod, claim *v1.PersistentVolumeClaim, err error) {
	if err == nil {
		reason := fmt.Sprintf("Successful%s", strings.Title(verb))
		message := fmt.Sprintf("%s Claim %s Pod %s in StatefulSet %s success",
			strings.ToLower(verb), claim.Name, pod.Name, set.Name)
		spc.recorder.Event(set, v1.EventTypeNormal, reason, message)
	} else {
		reason := fmt.Sprintf("Failed%s", strings.Title(verb))
		message := fmt.Sprintf("%s Claim %s for Pod %s in StatefulSet %s failed error: %s",
			strings.ToLower(verb), claim.Name, pod.Name, set.Name, err)
		spc.recorder.Event(set, v1.EventTypeWarning, reason, message)
	}
}

// createPersistentVolumeClaims creates all of the required PersistentVolumeClaims for pod, which mush be a member of
// set. If all of the claims for Pod are successfully created, the returned error is nil. If creation fails, this method
// may be called again until no error is returned, indicating the PersistentVolumeClaims for pod are consistent with
// set's Spec.
func (spc *realStatefulPodControl) createPersistentVolumeClaims(set *apps.StatefulSet, pod *v1.Pod) error {
	var errs []error
	for _, claim := range getPersistentVolumeClaims(set, pod) {
		_, err := spc.pvcLister.PersistentVolumeClaims(claim.Namespace).Get(claim.Name)
		switch {
		case apierrors.IsNotFound(err):
			_, err := spc.client.Core().PersistentVolumeClaims(claim.Namespace).Create(&claim)
			if err != nil {
				errs = append(errs, fmt.Errorf("Failed to create PVC %s: %s", claim.Name, err))
			}
			if err == nil || !apierrors.IsAlreadyExists(err) {
				spc.recordClaimEvent("create", set, pod, &claim, err)
			}
		case err != nil:
			errs = append(errs, fmt.Errorf("Failed to retrieve PVC %s: %s", claim.Name, err))
			spc.recordClaimEvent("create", set, pod, &claim, err)
		}
		// TODO: Check resource requirements and accessmodes, update if necessary
	}
	return errorutils.NewAggregate(errs)
}

var _ StatefulPodControlInterface = &realStatefulPodControl{}
<|endoftext|>


<commit_before>package rpc

import (
	"encoding/json"
	"xd/lib/bittorrent/swarm"
)

type AddTorrentRequest struct {
	BaseRequest
	URL string `json:"url"`
}

func (atr *AddTorrentRequest) ProcessRequest(sw *swarm.Swarm, w *ResponseWriter) {
	sw.AddRemoteTorrent(atr.URL)
	w.Return(map[string]interface{}{"error": nil})
}

func (atr *AddTorrentRequest) MarshalJSON() (data []byte, err error) {
	data, err = json.Marshal(map[string]interface{}{
		ParamSwarm:  atr.Swarm,
		ParamURL:    atr.URL,
		ParamMethod: RPCAddTorrent,
	})
	return
}
<commit_msg>fix issue #29<commit_after>package rpc

import (
	"encoding/json"
	"xd/lib/bittorrent/swarm"
)

type AddTorrentRequest struct {
	BaseRequest
	URL string `json:"url"`
}

func (atr *AddTorrentRequest) ProcessRequest(sw *swarm.Swarm, w *ResponseWriter) {
	go sw.AddRemoteTorrent(atr.URL)
	w.Return(map[string]interface{}{"error": nil})
}

func (atr *AddTorrentRequest) MarshalJSON() (data []byte, err error) {
	data, err = json.Marshal(map[string]interface{}{
		ParamSwarm:  atr.Swarm,
		ParamURL:    atr.URL,
		ParamMethod: RPCAddTorrent,
	})
	return
}
<|endoftext|>