<commit_before>// Copyright 2018-2019 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.

// +build !privileged_tests

package store

import (
	"context"
	"encoding/json"
	"fmt"
	"testing"
	"time"

	"github.com/cilium/cilium/pkg/defaults"
	"github.com/cilium/cilium/pkg/kvstore"
	"github.com/cilium/cilium/pkg/lock"
	"github.com/cilium/cilium/pkg/option"
	"github.com/cilium/cilium/pkg/testutils"

	. "gopkg.in/check.v1"
)

const (
	testPrefix = "store-tests"
)

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

type StoreSuite struct{}

type StoreEtcdSuite struct {
	StoreSuite
}

var _ = Suite(&StoreEtcdSuite{})

func (e *StoreEtcdSuite) SetUpTest(c *C) {
	kvstore.SetupDummy("etcd")
}

func (e *StoreEtcdSuite) TearDownTest(c *C) {
	kvstore.Client().DeletePrefix(context.TODO(), testPrefix)
	kvstore.Client().Close()
}

type StoreConsulSuite struct {
	StoreSuite
}

var _ = Suite(&StoreConsulSuite{})

func (e *StoreConsulSuite) SetUpTest(c *C) {
	kvstore.SetupDummy("consul")
}

func (e *StoreConsulSuite) TearDownTest(c *C) {
	kvstore.Client().DeletePrefix(context.TODO(), testPrefix)
	kvstore.Client().Close()
	time.Sleep(defaults.NodeDeleteDelay + 5*time.Second)
}

type TestType struct {
	Name string
}

var _ = TestType{}

func (t *TestType) GetKeyName() string          { return t.Name }
func (t *TestType) DeepKeyCopy() LocalKey       { return &TestType{Name: t.Name} }
func (t *TestType) Marshal() ([]byte, error)    { return json.Marshal(t) }
func (t *TestType) Unmarshal(data []byte) error { return json.Unmarshal(data, t) }

type opCounter struct {
	deleted int
	updated int
}

var (
	counter     = map[string]*opCounter{}
	counterLock lock.RWMutex
)

func (t *TestType) deleted() int {
	counterLock.RLock()
	defer counterLock.RUnlock()
	return counter[t.Name].deleted
}

func (t *TestType) updated() int {
	counterLock.RLock()
	defer counterLock.RUnlock()
	return counter[t.Name].updated
}

func initTestType(name string) TestType {
	t := TestType{}
	t.Name = name
	counterLock.Lock()
	counter[name] = &opCounter{}
	counterLock.Unlock()
	return t
}

type observer struct{}

func (o *observer) OnUpdate(k Key) {
	counterLock.Lock()
	if c, ok := counter[k.(*TestType).Name]; ok {
		c.updated++
	}
	counterLock.Unlock()
}
func (o *observer) OnDelete(k NamedKey) {
	counterLock.Lock()
	counter[k.(*TestType).Name].deleted++
	counterLock.Unlock()
}

func newTestType() Key {
	t := TestType{}
	return &t
}

func (s *StoreSuite) TestStoreCreation(c *C) {
	// Missing Prefix must result in error
	store, err := JoinSharedStore(Configuration{})
	c.Assert(err, ErrorMatches, "prefix must be specified")
	c.Assert(store, IsNil)

	// Missing KeyCreator must result in error
	store, err = JoinSharedStore(Configuration{Prefix: testutils.RandomRune()})
	c.Assert(err, ErrorMatches, "KeyCreator must be specified")
	c.Assert(store, IsNil)

	// Basic creation should result in default values
	store, err = JoinSharedStore(Configuration{Prefix: testutils.RandomRune(), KeyCreator: newTestType})
	c.Assert(err, IsNil)
	c.Assert(store, Not(IsNil))
	c.Assert(store.conf.SynchronizationInterval, Equals, option.Config.KVstorePeriodicSync)
	store.Close(context.TODO())

	// Test with kvstore client specified
	store, err = JoinSharedStore(Configuration{Prefix: testutils.RandomRune(), KeyCreator: newTestType, Backend: kvstore.Client()})
	c.Assert(err, IsNil)
	c.Assert(store, Not(IsNil))
	c.Assert(store.conf.SynchronizationInterval, Equals, option.Config.KVstorePeriodicSync)
	store.Close(context.TODO())
}

func expect(check func() bool) error {
	start := time.Now()
	for {
		if check() {
			return nil
		}

		if time.Since(start) > defaults.NodeDeleteDelay+10*time.Second {
			return fmt.Errorf("timeout while waiting for expected value")
		}

		time.Sleep(10 * time.Millisecond)
	}
}

func (s *StoreSuite) TestStoreOperations(c *C) {
	// Basic creation should result in default values
	store, err := JoinSharedStore(Configuration{Prefix: testutils.RandomRune(), KeyCreator: newTestType, Observer: &observer{}})
	c.Assert(err, IsNil)
	c.Assert(store, Not(IsNil))
	defer store.Close(context.TODO())

	localKey1 := initTestType("local1")
	localKey2 := initTestType("local2")
	localKey3 := initTestType("local3")

	err = store.UpdateLocalKeySync(context.TODO(), &localKey1)
	c.Assert(err, IsNil)
	err = store.UpdateLocalKeySync(context.TODO(), &localKey2)
	c.Assert(err, IsNil)

	// due to the short sync interval, it is possible that multiple updates
	// have occurred, make the test reliable by succeeding on at lest one
	// update
	c.Assert(expect(func() bool { return localKey1.updated() >= 1 }), IsNil)
	c.Assert(expect(func() bool { return localKey2.updated() >= 1 }), IsNil)
	c.Assert(expect(func() bool { return localKey3.updated() == 0 }), IsNil)

	store.DeleteLocalKey(context.TODO(), &localKey1)
	// localKey1 will be deleted 2 times, one from local key and other from
	// the kvstore watcher
	c.Assert(expect(func() bool { return localKey1.deleted() == 2 }), IsNil)
	c.Assert(expect(func() bool { return localKey2.deleted() == 0 }), IsNil)
	c.Assert(expect(func() bool { return localKey3.deleted() == 0 }), IsNil)

	store.DeleteLocalKey(context.TODO(), &localKey3)
	// localKey3 won't be deleted because it was never added
	c.Assert(expect(func() bool { return localKey3.deleted() == 0 }), IsNil)

	store.DeleteLocalKey(context.TODO(), &localKey2)
	c.Assert(expect(func() bool { return localKey1.deleted() == 2 }), IsNil)
	// localKey2 will be deleted 2 times, one from local key and other from
	// the kvstore watcher
	c.Assert(expect(func() bool { return localKey2.deleted() == 2 }), IsNil)
	c.Assert(expect(func() bool { return localKey3.deleted() == 0 }), IsNil)
}

func (s *StoreSuite) TestStorePeriodicSync(c *C) {
	// Create a store with a very short periodic sync interval
	store, err := JoinSharedStore(Configuration{
		Prefix:                  testutils.RandomRune(),
		KeyCreator:              newTestType,
		SynchronizationInterval: 10 * time.Millisecond,
		Observer:                &observer{},
	})
	c.Assert(err, IsNil)
	c.Assert(store, Not(IsNil))
	defer store.Close(context.TODO())

	localKey1 := initTestType("local1")
	localKey2 := initTestType("local2")

	err = store.UpdateLocalKeySync(context.TODO(), &localKey1)
	c.Assert(err, IsNil)
	err = store.UpdateLocalKeySync(context.TODO(), &localKey2)
	c.Assert(err, IsNil)

	c.Assert(expect(func() bool { return localKey1.updated() >= 1 }), IsNil)
	c.Assert(expect(func() bool { return localKey2.updated() >= 1 }), IsNil)

	store.DeleteLocalKey(context.TODO(), &localKey1)
	store.DeleteLocalKey(context.TODO(), &localKey2)

	c.Assert(expect(func() bool { return localKey1.deleted() == 1 }), IsNil)
	c.Assert(expect(func() bool { return localKey2.deleted() == 1 }), IsNil)
}

func (s *StoreSuite) TestStoreLocalKeyProtection(c *C) {
	store, err := JoinSharedStore(Configuration{
		Prefix:                  testutils.RandomRune(),
		KeyCreator:              newTestType,
		SynchronizationInterval: time.Hour, // ensure that periodic sync does not interfer
		Observer:                &observer{},
	})
	c.Assert(err, IsNil)
	c.Assert(store, Not(IsNil))
	defer store.Close(context.TODO())

	localKey1 := initTestType("local1")

	err = store.UpdateLocalKeySync(context.TODO(), &localKey1)
	c.Assert(err, IsNil)

	c.Assert(expect(func() bool { return localKey1.updated() >= 1 }), IsNil)
	// delete all keys
	kvstore.Client().DeletePrefix(context.TODO(), store.conf.Prefix)
	c.Assert(expect(func() bool {
		v, err := kvstore.Client().Get(context.TODO(), store.keyPath(&localKey1))
		return err == nil && v == nil
	}), IsNil)
}

func setupStoreCollaboration(c *C, storePrefix, keyPrefix string) *SharedStore {
	store, err := JoinSharedStore(Configuration{
		Prefix:                  storePrefix,
		KeyCreator:              newTestType,
		SynchronizationInterval: time.Second,
		Observer:                &observer{},
	})
	c.Assert(err, IsNil)
	c.Assert(store, Not(IsNil))

	localKey1 := initTestType(keyPrefix + "-local1")
	err = store.UpdateLocalKeySync(context.TODO(), &localKey1)
	c.Assert(err, IsNil)

	localKey2 := initTestType(keyPrefix + "-local2")
	err = store.UpdateLocalKeySync(context.TODO(), &localKey2)
	c.Assert(err, IsNil)

	// wait until local keys was inserted and until the kvstore has confirmed the
	c.Assert(expect(func() bool { return localKey1.updated() >= 1 }), IsNil)
	c.Assert(expect(func() bool { return localKey2.updated() >= 1 }), IsNil)

	c.Assert(len(store.getLocalKeys()), Equals, 2)

	return store
}

func (s *StoreSuite) TestStoreCollaboration(c *C) {
	storePrefix := testutils.RandomRune()

	collab1 := setupStoreCollaboration(c, storePrefix, testutils.RandomRune())
	defer collab1.Close(context.TODO())

	collab2 := setupStoreCollaboration(c, storePrefix, testutils.RandomRune())
	defer collab2.Close(context.TODO())

	c.Assert(expect(func() bool {
		totalKeys := len(collab1.getLocalKeys()) + len(collab2.getLocalKeys())
		keys1, keys2 := collab1.getSharedKeys(), collab2.getSharedKeys()

		log.Debugf("totalKeys %d == keys1 %d == keys2 %d", totalKeys, len(keys1), len(keys2))
		return len(keys1) == totalKeys && len(keys1) == len(keys2)
	}), IsNil)
}
<commit_msg>.travis:fix test failed caused by timeout<commit_after>// Copyright 2018-2019 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.

// +build !privileged_tests

package store

import (
	"context"
	"encoding/json"
	"fmt"
	"testing"
	"time"

	"github.com/cilium/cilium/pkg/defaults"
	"github.com/cilium/cilium/pkg/kvstore"
	"github.com/cilium/cilium/pkg/lock"
	"github.com/cilium/cilium/pkg/option"
	"github.com/cilium/cilium/pkg/testutils"

	. "gopkg.in/check.v1"
)

const (
	testPrefix = "store-tests"
)

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

type StoreSuite struct{}

type StoreEtcdSuite struct {
	StoreSuite
}

var _ = Suite(&StoreEtcdSuite{})

func (e *StoreEtcdSuite) SetUpTest(c *C) {
	kvstore.SetupDummy("etcd")
}

func (e *StoreEtcdSuite) TearDownTest(c *C) {
	kvstore.Client().DeletePrefix(context.TODO(), testPrefix)
	kvstore.Client().Close()
}

type StoreConsulSuite struct {
	StoreSuite
}

var _ = Suite(&StoreConsulSuite{})

func (e *StoreConsulSuite) SetUpTest(c *C) {
	kvstore.SetupDummy("consul")
}

func (e *StoreConsulSuite) TearDownTest(c *C) {
	kvstore.Client().DeletePrefix(context.TODO(), testPrefix)
	kvstore.Client().Close()
	time.Sleep(defaults.NodeDeleteDelay + 5*time.Second)
}

type TestType struct {
	Name string
}

var _ = TestType{}

func (t *TestType) GetKeyName() string          { return t.Name }
func (t *TestType) DeepKeyCopy() LocalKey       { return &TestType{Name: t.Name} }
func (t *TestType) Marshal() ([]byte, error)    { return json.Marshal(t) }
func (t *TestType) Unmarshal(data []byte) error { return json.Unmarshal(data, t) }

type opCounter struct {
	deleted int
	updated int
}

var (
	counter     = map[string]*opCounter{}
	counterLock lock.RWMutex
)

func (t *TestType) deleted() int {
	counterLock.RLock()
	defer counterLock.RUnlock()
	return counter[t.Name].deleted
}

func (t *TestType) updated() int {
	counterLock.RLock()
	defer counterLock.RUnlock()
	return counter[t.Name].updated
}

func initTestType(name string) TestType {
	t := TestType{}
	t.Name = name
	counterLock.Lock()
	counter[name] = &opCounter{}
	counterLock.Unlock()
	return t
}

type observer struct{}

func (o *observer) OnUpdate(k Key) {
	counterLock.Lock()
	if c, ok := counter[k.(*TestType).Name]; ok {
		c.updated++
	}
	counterLock.Unlock()
}
func (o *observer) OnDelete(k NamedKey) {
	counterLock.Lock()
	counter[k.(*TestType).Name].deleted++
	counterLock.Unlock()
}

func newTestType() Key {
	t := TestType{}
	return &t
}

func (s *StoreSuite) TestStoreCreation(c *C) {
	// Missing Prefix must result in error
	store, err := JoinSharedStore(Configuration{})
	c.Assert(err, ErrorMatches, "prefix must be specified")
	c.Assert(store, IsNil)

	// Missing KeyCreator must result in error
	store, err = JoinSharedStore(Configuration{Prefix: testutils.RandomRune()})
	c.Assert(err, ErrorMatches, "KeyCreator must be specified")
	c.Assert(store, IsNil)

	// Basic creation should result in default values
	store, err = JoinSharedStore(Configuration{Prefix: testutils.RandomRune(), KeyCreator: newTestType})
	c.Assert(err, IsNil)
	c.Assert(store, Not(IsNil))
	c.Assert(store.conf.SynchronizationInterval, Equals, option.Config.KVstorePeriodicSync)
	store.Close(context.TODO())

	// Test with kvstore client specified
	store, err = JoinSharedStore(Configuration{Prefix: testutils.RandomRune(), KeyCreator: newTestType, Backend: kvstore.Client()})
	c.Assert(err, IsNil)
	c.Assert(store, Not(IsNil))
	c.Assert(store.conf.SynchronizationInterval, Equals, option.Config.KVstorePeriodicSync)
	store.Close(context.TODO())
}

func expect(check func() bool) error {
	start := time.Now()
	for {
		if check() {
			return nil
		}

		if time.Since(start) > defaults.NodeDeleteDelay+10*time.Second {
			return fmt.Errorf("timeout while waiting for expected value")
		}

		time.Sleep(10 * time.Millisecond)
	}
}

func (s *StoreSuite) TestStoreOperations(c *C) {
	// Basic creation should result in default values
	store, err := JoinSharedStore(Configuration{Prefix: testutils.RandomRune(), KeyCreator: newTestType, Observer: &observer{}})
	c.Assert(err, IsNil)
	c.Assert(store, Not(IsNil))
	defer store.Close(context.TODO())

	localKey1 := initTestType("local1")
	localKey2 := initTestType("local2")
	localKey3 := initTestType("local3")

	err = store.UpdateLocalKeySync(context.TODO(), &localKey1)
	c.Assert(err, IsNil)
	err = store.UpdateLocalKeySync(context.TODO(), &localKey2)
	c.Assert(err, IsNil)

	// due to the short sync interval, it is possible that multiple updates
	// have occurred, make the test reliable by succeeding on at lest one
	// update
	c.Assert(expect(func() bool { return localKey1.updated() >= 1 }), IsNil)
	c.Assert(expect(func() bool { return localKey2.updated() >= 1 }), IsNil)
	c.Assert(expect(func() bool { return localKey3.updated() == 0 }), IsNil)

	store.DeleteLocalKey(context.TODO(), &localKey1)
	// localKey1 will be deleted 2 times, one from local key and other from
	// the kvstore watcher
	c.Assert(expect(func() bool { return localKey1.deleted() == 2 }), IsNil)
	c.Assert(expect(func() bool { return localKey2.deleted() == 0 }), IsNil)
	c.Assert(expect(func() bool { return localKey3.deleted() == 0 }), IsNil)

	store.DeleteLocalKey(context.TODO(), &localKey3)
	// localKey3 won't be deleted because it was never added
	c.Assert(expect(func() bool { return localKey3.deleted() == 0 }), IsNil)

	store.DeleteLocalKey(context.TODO(), &localKey2)
	c.Assert(expect(func() bool { return localKey1.deleted() == 2 }), IsNil)
	// localKey2 will be deleted 2 times, one from local key and other from
	// the kvstore watcher
	c.Assert(expect(func() bool { return localKey2.deleted() == 2 }), IsNil)
	c.Assert(expect(func() bool { return localKey3.deleted() == 0 }), IsNil)
}

func (s *StoreSuite) TestStorePeriodicSync(c *C) {
	// Create a store with a very short periodic sync interval
	store, err := JoinSharedStore(Configuration{
		Prefix:                  testutils.RandomRune(),
		KeyCreator:              newTestType,
		SynchronizationInterval: 10 * time.Millisecond,
		Observer:                &observer{},
	})
	c.Assert(err, IsNil)
	c.Assert(store, Not(IsNil))
	defer store.Close(context.TODO())

	localKey1 := initTestType("local1")
	localKey2 := initTestType("local2")

	err = store.UpdateLocalKeySync(context.TODO(), &localKey1)
	c.Assert(err, IsNil)
	err = store.UpdateLocalKeySync(context.TODO(), &localKey2)
	c.Assert(err, IsNil)

	c.Assert(expect(func() bool { return localKey1.updated() >= 1 }), IsNil)
	c.Assert(expect(func() bool { return localKey2.updated() >= 1 }), IsNil)

	store.DeleteLocalKey(context.TODO(), &localKey1)
	store.DeleteLocalKey(context.TODO(), &localKey2)

	c.Assert(expect(func() bool { return localKey1.deleted() == 1 }), IsNil)
	c.Assert(expect(func() bool { return localKey2.deleted() == 1 }), IsNil)
}

func (s *StoreSuite) TestStoreLocalKeyProtection(c *C) {
	store, err := JoinSharedStore(Configuration{
		Prefix:                  testutils.RandomRune(),
		KeyCreator:              newTestType,
		SynchronizationInterval: time.Hour, // ensure that periodic sync does not interfer
		Observer:                &observer{},
	})
	c.Assert(err, IsNil)
	c.Assert(store, Not(IsNil))
	defer store.Close(context.TODO())

	localKey1 := initTestType("local1")

	err = store.UpdateLocalKeySync(context.TODO(), &localKey1)
	c.Assert(err, IsNil)

	c.Assert(expect(func() bool { return localKey1.updated() >= 1 }), IsNil)
	// delete all keys
	kvstore.Client().DeletePrefix(context.TODO(), store.conf.Prefix)
	time.Sleep(10 * time.Millisecond)
	c.Assert(expect(func() bool {
		v, err := kvstore.Client().Get(context.TODO(), store.keyPath(&localKey1))
		return err == nil && v != nil
	}), IsNil)
}

func setupStoreCollaboration(c *C, storePrefix, keyPrefix string) *SharedStore {
	store, err := JoinSharedStore(Configuration{
		Prefix:                  storePrefix,
		KeyCreator:              newTestType,
		SynchronizationInterval: time.Second,
		Observer:                &observer{},
	})
	c.Assert(err, IsNil)
	c.Assert(store, Not(IsNil))

	localKey1 := initTestType(keyPrefix + "-local1")
	err = store.UpdateLocalKeySync(context.TODO(), &localKey1)
	c.Assert(err, IsNil)

	localKey2 := initTestType(keyPrefix + "-local2")
	err = store.UpdateLocalKeySync(context.TODO(), &localKey2)
	c.Assert(err, IsNil)

	// wait until local keys was inserted and until the kvstore has confirmed the
	c.Assert(expect(func() bool { return localKey1.updated() >= 1 }), IsNil)
	c.Assert(expect(func() bool { return localKey2.updated() >= 1 }), IsNil)

	c.Assert(len(store.getLocalKeys()), Equals, 2)

	return store
}

func (s *StoreSuite) TestStoreCollaboration(c *C) {
	storePrefix := testutils.RandomRune()

	collab1 := setupStoreCollaboration(c, storePrefix, testutils.RandomRune())
	defer collab1.Close(context.TODO())

	collab2 := setupStoreCollaboration(c, storePrefix, testutils.RandomRune())
	defer collab2.Close(context.TODO())

	c.Assert(expect(func() bool {
		totalKeys := len(collab1.getLocalKeys()) + len(collab2.getLocalKeys())
		keys1, keys2 := collab1.getSharedKeys(), collab2.getSharedKeys()

		log.Debugf("totalKeys %d == keys1 %d == keys2 %d", totalKeys, len(keys1), len(keys2))
		return len(keys1) == totalKeys && len(keys1) == len(keys2)
	}), IsNil)
}
<|endoftext|>


<commit_before>// Copyright 2018 The gVisor 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 kernel

import (
	"fmt"

	"gvisor.dev/gvisor/pkg/context"
	"gvisor.dev/gvisor/pkg/sentry/fs"
	"gvisor.dev/gvisor/pkg/sentry/vfs"
	"gvisor.dev/gvisor/pkg/sync"
)

// FSContext contains filesystem context.
//
// This includes umask and working directory.
//
// +stateify savable
type FSContext struct {
	FSContextRefs

	// mu protects below.
	mu sync.Mutex `state:"nosave"`

	// root is the filesystem root. Will be nil iff the FSContext has been
	// destroyed.
	root *fs.Dirent

	// rootVFS2 is the filesystem root.
	rootVFS2 vfs.VirtualDentry

	// cwd is the current working directory. Will be nil iff the FSContext
	// has been destroyed.
	cwd *fs.Dirent

	// cwdVFS2 is the current working directory.
	cwdVFS2 vfs.VirtualDentry

	// umask is the current file mode creation mask. When a thread using this
	// context invokes a syscall that creates a file, bits set in umask are
	// removed from the permissions that the file is created with.
	umask uint
}

// newFSContext returns a new filesystem context.
func newFSContext(root, cwd *fs.Dirent, umask uint) *FSContext {
	root.IncRef()
	cwd.IncRef()
	f := FSContext{
		root:  root,
		cwd:   cwd,
		umask: umask,
	}
	f.EnableLeakCheck()
	return &f
}

// NewFSContextVFS2 returns a new filesystem context.
func NewFSContextVFS2(root, cwd vfs.VirtualDentry, umask uint) *FSContext {
	root.IncRef()
	cwd.IncRef()
	f := FSContext{
		rootVFS2: root,
		cwdVFS2:  cwd,
		umask:    umask,
	}
	f.EnableLeakCheck()
	return &f
}

// DecRef implements RefCounter.DecRef.
//
// When f reaches zero references, DecRef will be called on both root and cwd
// Dirents.
//
// Note that there may still be calls to WorkingDirectory() or RootDirectory()
// (that return nil).  This is because valid references may still be held via
// proc files or other mechanisms.
func (f *FSContext) DecRef(ctx context.Context) {
	f.FSContextRefs.DecRef(func() {
		// Hold f.mu so that we don't race with RootDirectory() and
		// WorkingDirectory().
		f.mu.Lock()
		defer f.mu.Unlock()

		if VFS2Enabled {
			f.rootVFS2.DecRef(ctx)
			f.rootVFS2 = vfs.VirtualDentry{}
			f.cwdVFS2.DecRef(ctx)
			f.cwdVFS2 = vfs.VirtualDentry{}
		} else {
			f.root.DecRef(ctx)
			f.root = nil
			f.cwd.DecRef(ctx)
			f.cwd = nil
		}
	})
}

// Fork forks this FSContext.
//
// This is not a valid call after f is destroyed.
func (f *FSContext) Fork() *FSContext {
	f.mu.Lock()
	defer f.mu.Unlock()

	if VFS2Enabled {
		if !f.cwdVFS2.Ok() {
			panic("FSContext.Fork() called after destroy")
		}
		f.cwdVFS2.IncRef()
		f.rootVFS2.IncRef()
	} else {
		if f.cwd == nil {
			panic("FSContext.Fork() called after destroy")
		}
		f.cwd.IncRef()
		f.root.IncRef()
	}

	return &FSContext{
		cwd:      f.cwd,
		root:     f.root,
		cwdVFS2:  f.cwdVFS2,
		rootVFS2: f.rootVFS2,
		umask:    f.umask,
	}
}

// WorkingDirectory returns the current working directory.
//
// This will return nil if called after f is destroyed, otherwise it will return
// a Dirent with a reference taken.
func (f *FSContext) WorkingDirectory() *fs.Dirent {
	f.mu.Lock()
	defer f.mu.Unlock()

	f.cwd.IncRef()
	return f.cwd
}

// WorkingDirectoryVFS2 returns the current working directory.
//
// This will return nil if called after f is destroyed, otherwise it will return
// a Dirent with a reference taken.
func (f *FSContext) WorkingDirectoryVFS2() vfs.VirtualDentry {
	f.mu.Lock()
	defer f.mu.Unlock()

	f.cwdVFS2.IncRef()
	return f.cwdVFS2
}

// SetWorkingDirectory sets the current working directory.
// This will take an extra reference on the Dirent.
//
// This is not a valid call after f is destroyed.
func (f *FSContext) SetWorkingDirectory(ctx context.Context, d *fs.Dirent) {
	if d == nil {
		panic("FSContext.SetWorkingDirectory called with nil dirent")
	}

	f.mu.Lock()
	defer f.mu.Unlock()

	if f.cwd == nil {
		panic(fmt.Sprintf("FSContext.SetWorkingDirectory(%v)) called after destroy", d))
	}

	old := f.cwd
	f.cwd = d
	d.IncRef()
	old.DecRef(ctx)
}

// SetWorkingDirectoryVFS2 sets the current working directory.
// This will take an extra reference on the VirtualDentry.
//
// This is not a valid call after f is destroyed.
func (f *FSContext) SetWorkingDirectoryVFS2(ctx context.Context, d vfs.VirtualDentry) {
	f.mu.Lock()
	defer f.mu.Unlock()

	if !f.cwdVFS2.Ok() {
		panic(fmt.Sprintf("FSContext.SetWorkingDirectoryVFS2(%v)) called after destroy", d))
	}

	old := f.cwdVFS2
	f.cwdVFS2 = d
	d.IncRef()
	old.DecRef(ctx)
}

// RootDirectory returns the current filesystem root.
//
// This will return nil if called after f is destroyed, otherwise it will return
// a Dirent with a reference taken.
func (f *FSContext) RootDirectory() *fs.Dirent {
	f.mu.Lock()
	defer f.mu.Unlock()
	if f.root != nil {
		f.root.IncRef()
	}
	return f.root
}

// RootDirectoryVFS2 returns the current filesystem root.
//
// This will return nil if called after f is destroyed, otherwise it will return
// a Dirent with a reference taken.
func (f *FSContext) RootDirectoryVFS2() vfs.VirtualDentry {
	f.mu.Lock()
	defer f.mu.Unlock()

	f.rootVFS2.IncRef()
	return f.rootVFS2
}

// SetRootDirectory sets the root directory.
// This will take an extra reference on the Dirent.
//
// This is not a valid call after f is destroyed.
func (f *FSContext) SetRootDirectory(ctx context.Context, d *fs.Dirent) {
	if d == nil {
		panic("FSContext.SetRootDirectory called with nil dirent")
	}

	f.mu.Lock()
	defer f.mu.Unlock()

	if f.root == nil {
		panic(fmt.Sprintf("FSContext.SetRootDirectory(%v)) called after destroy", d))
	}

	old := f.root
	f.root = d
	d.IncRef()
	old.DecRef(ctx)
}

// SetRootDirectoryVFS2 sets the root directory. It takes a reference on vd.
//
// This is not a valid call after f is destroyed.
func (f *FSContext) SetRootDirectoryVFS2(ctx context.Context, vd vfs.VirtualDentry) {
	if !vd.Ok() {
		panic("FSContext.SetRootDirectoryVFS2 called with zero-value VirtualDentry")
	}

	f.mu.Lock()

	if !f.rootVFS2.Ok() {
		f.mu.Unlock()
		panic(fmt.Sprintf("FSContext.SetRootDirectoryVFS2(%v)) called after destroy", vd))
	}

	old := f.rootVFS2
	vd.IncRef()
	f.rootVFS2 = vd
	f.mu.Unlock()
	old.DecRef(ctx)
}

// Umask returns the current umask.
func (f *FSContext) Umask() uint {
	f.mu.Lock()
	defer f.mu.Unlock()
	return f.umask
}

// SwapUmask atomically sets the current umask and returns the old umask.
func (f *FSContext) SwapUmask(mask uint) uint {
	f.mu.Lock()
	defer f.mu.Unlock()
	old := f.umask
	f.umask = mask
	return old
}
<commit_msg>Check for nil in kernel.FSContext functions.<commit_after>// Copyright 2018 The gVisor 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 kernel

import (
	"fmt"

	"gvisor.dev/gvisor/pkg/context"
	"gvisor.dev/gvisor/pkg/sentry/fs"
	"gvisor.dev/gvisor/pkg/sentry/vfs"
	"gvisor.dev/gvisor/pkg/sync"
)

// FSContext contains filesystem context.
//
// This includes umask and working directory.
//
// +stateify savable
type FSContext struct {
	FSContextRefs

	// mu protects below.
	mu sync.Mutex `state:"nosave"`

	// root is the filesystem root. Will be nil iff the FSContext has been
	// destroyed.
	root *fs.Dirent

	// rootVFS2 is the filesystem root.
	rootVFS2 vfs.VirtualDentry

	// cwd is the current working directory. Will be nil iff the FSContext
	// has been destroyed.
	cwd *fs.Dirent

	// cwdVFS2 is the current working directory.
	cwdVFS2 vfs.VirtualDentry

	// umask is the current file mode creation mask. When a thread using this
	// context invokes a syscall that creates a file, bits set in umask are
	// removed from the permissions that the file is created with.
	umask uint
}

// newFSContext returns a new filesystem context.
func newFSContext(root, cwd *fs.Dirent, umask uint) *FSContext {
	root.IncRef()
	cwd.IncRef()
	f := FSContext{
		root:  root,
		cwd:   cwd,
		umask: umask,
	}
	f.EnableLeakCheck()
	return &f
}

// NewFSContextVFS2 returns a new filesystem context.
func NewFSContextVFS2(root, cwd vfs.VirtualDentry, umask uint) *FSContext {
	root.IncRef()
	cwd.IncRef()
	f := FSContext{
		rootVFS2: root,
		cwdVFS2:  cwd,
		umask:    umask,
	}
	f.EnableLeakCheck()
	return &f
}

// DecRef implements RefCounter.DecRef.
//
// When f reaches zero references, DecRef will be called on both root and cwd
// Dirents.
//
// Note that there may still be calls to WorkingDirectory() or RootDirectory()
// (that return nil).  This is because valid references may still be held via
// proc files or other mechanisms.
func (f *FSContext) DecRef(ctx context.Context) {
	f.FSContextRefs.DecRef(func() {
		// Hold f.mu so that we don't race with RootDirectory() and
		// WorkingDirectory().
		f.mu.Lock()
		defer f.mu.Unlock()

		if VFS2Enabled {
			f.rootVFS2.DecRef(ctx)
			f.rootVFS2 = vfs.VirtualDentry{}
			f.cwdVFS2.DecRef(ctx)
			f.cwdVFS2 = vfs.VirtualDentry{}
		} else {
			f.root.DecRef(ctx)
			f.root = nil
			f.cwd.DecRef(ctx)
			f.cwd = nil
		}
	})
}

// Fork forks this FSContext.
//
// This is not a valid call after f is destroyed.
func (f *FSContext) Fork() *FSContext {
	f.mu.Lock()
	defer f.mu.Unlock()

	if VFS2Enabled {
		if !f.cwdVFS2.Ok() {
			panic("FSContext.Fork() called after destroy")
		}
		f.cwdVFS2.IncRef()
		f.rootVFS2.IncRef()
	} else {
		if f.cwd == nil {
			panic("FSContext.Fork() called after destroy")
		}
		f.cwd.IncRef()
		f.root.IncRef()
	}

	return &FSContext{
		cwd:      f.cwd,
		root:     f.root,
		cwdVFS2:  f.cwdVFS2,
		rootVFS2: f.rootVFS2,
		umask:    f.umask,
	}
}

// WorkingDirectory returns the current working directory.
//
// This will return nil if called after f is destroyed, otherwise it will return
// a Dirent with a reference taken.
func (f *FSContext) WorkingDirectory() *fs.Dirent {
	f.mu.Lock()
	defer f.mu.Unlock()

	if f.cwd != nil {
		f.cwd.IncRef()
	}
	return f.cwd
}

// WorkingDirectoryVFS2 returns the current working directory.
//
// This will return an empty vfs.VirtualDentry if called after f is
// destroyed, otherwise it will return a Dirent with a reference taken.
func (f *FSContext) WorkingDirectoryVFS2() vfs.VirtualDentry {
	f.mu.Lock()
	defer f.mu.Unlock()

	if f.cwdVFS2.Ok() {
		f.cwdVFS2.IncRef()
	}
	return f.cwdVFS2
}

// SetWorkingDirectory sets the current working directory.
// This will take an extra reference on the Dirent.
//
// This is not a valid call after f is destroyed.
func (f *FSContext) SetWorkingDirectory(ctx context.Context, d *fs.Dirent) {
	if d == nil {
		panic("FSContext.SetWorkingDirectory called with nil dirent")
	}

	f.mu.Lock()
	defer f.mu.Unlock()

	if f.cwd == nil {
		panic(fmt.Sprintf("FSContext.SetWorkingDirectory(%v)) called after destroy", d))
	}

	old := f.cwd
	f.cwd = d
	d.IncRef()
	old.DecRef(ctx)
}

// SetWorkingDirectoryVFS2 sets the current working directory.
// This will take an extra reference on the VirtualDentry.
//
// This is not a valid call after f is destroyed.
func (f *FSContext) SetWorkingDirectoryVFS2(ctx context.Context, d vfs.VirtualDentry) {
	f.mu.Lock()
	defer f.mu.Unlock()

	if !f.cwdVFS2.Ok() {
		panic(fmt.Sprintf("FSContext.SetWorkingDirectoryVFS2(%v)) called after destroy", d))
	}

	old := f.cwdVFS2
	f.cwdVFS2 = d
	d.IncRef()
	old.DecRef(ctx)
}

// RootDirectory returns the current filesystem root.
//
// This will return nil if called after f is destroyed, otherwise it will return
// a Dirent with a reference taken.
func (f *FSContext) RootDirectory() *fs.Dirent {
	f.mu.Lock()
	defer f.mu.Unlock()
	if f.root != nil {
		f.root.IncRef()
	}
	return f.root
}

// RootDirectoryVFS2 returns the current filesystem root.
//
// This will return an empty vfs.VirtualDentry if called after f is
// destroyed, otherwise it will return a Dirent with a reference taken.
func (f *FSContext) RootDirectoryVFS2() vfs.VirtualDentry {
	f.mu.Lock()
	defer f.mu.Unlock()

	if f.rootVFS2.Ok() {
		f.rootVFS2.IncRef()
	}
	return f.rootVFS2
}

// SetRootDirectory sets the root directory.
// This will take an extra reference on the Dirent.
//
// This is not a valid call after f is destroyed.
func (f *FSContext) SetRootDirectory(ctx context.Context, d *fs.Dirent) {
	if d == nil {
		panic("FSContext.SetRootDirectory called with nil dirent")
	}

	f.mu.Lock()
	defer f.mu.Unlock()

	if f.root == nil {
		panic(fmt.Sprintf("FSContext.SetRootDirectory(%v)) called after destroy", d))
	}

	old := f.root
	f.root = d
	d.IncRef()
	old.DecRef(ctx)
}

// SetRootDirectoryVFS2 sets the root directory. It takes a reference on vd.
//
// This is not a valid call after f is destroyed.
func (f *FSContext) SetRootDirectoryVFS2(ctx context.Context, vd vfs.VirtualDentry) {
	if !vd.Ok() {
		panic("FSContext.SetRootDirectoryVFS2 called with zero-value VirtualDentry")
	}

	f.mu.Lock()

	if !f.rootVFS2.Ok() {
		f.mu.Unlock()
		panic(fmt.Sprintf("FSContext.SetRootDirectoryVFS2(%v)) called after destroy", vd))
	}

	old := f.rootVFS2
	vd.IncRef()
	f.rootVFS2 = vd
	f.mu.Unlock()
	old.DecRef(ctx)
}

// Umask returns the current umask.
func (f *FSContext) Umask() uint {
	f.mu.Lock()
	defer f.mu.Unlock()
	return f.umask
}

// SwapUmask atomically sets the current umask and returns the old umask.
func (f *FSContext) SwapUmask(mask uint) uint {
	f.mu.Lock()
	defer f.mu.Unlock()
	old := f.umask
	f.umask = mask
	return old
}
<|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 filters

import (
	"errors"
	"net/http"

	utilwaitgroup "k8s.io/apimachinery/pkg/util/waitgroup"
	"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
	apirequest "k8s.io/apiserver/pkg/endpoints/request"
)

// WithWaitGroup adds all non long-running requests to wait group, which is used for graceful shutdown.
func WithWaitGroup(handler http.Handler, longRunning apirequest.LongRunningRequestCheck, wg *utilwaitgroup.SafeWaitGroup) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		ctx := req.Context()
		requestInfo, ok := apirequest.RequestInfoFrom(ctx)
		if !ok {
			// if this happens, the handler chain isn't setup correctly because there is no request info
			responsewriters.InternalError(w, req, errors.New("no RequestInfo found in the context"))
			return
		}

		if !longRunning(req, requestInfo) {
			if err := wg.Add(1); err != nil {
				http.Error(w, "apiserver is shutting down.", http.StatusInternalServerError)
				return
			}
			defer wg.Done()
		}

		handler.ServeHTTP(w, req)
	})
}
<commit_msg>apiserver: add Retry-After header to response when apiserver is shutting down<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 filters

import (
	"errors"
	"fmt"
	"net/http"

	"k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/runtime"
	utilwaitgroup "k8s.io/apimachinery/pkg/util/waitgroup"
	"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
	apirequest "k8s.io/apiserver/pkg/endpoints/request"
	"k8s.io/client-go/kubernetes/scheme"
)

// WithWaitGroup adds all non long-running requests to wait group, which is used for graceful shutdown.
func WithWaitGroup(handler http.Handler, longRunning apirequest.LongRunningRequestCheck, wg *utilwaitgroup.SafeWaitGroup) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		ctx := req.Context()
		requestInfo, ok := apirequest.RequestInfoFrom(ctx)
		if !ok {
			// if this happens, the handler chain isn't setup correctly because there is no request info
			responsewriters.InternalError(w, req, errors.New("no RequestInfo found in the context"))
			return
		}

		if !longRunning(req, requestInfo) {
			if err := wg.Add(1); err != nil {
				// When apiserver is shutting down, signal clients to retry
				// There is a good chance the client hit a different server, so a tight retry is good for client responsiveness.
				w.Header().Add("Retry-After", "1")
				w.Header().Set("Content-Type", runtime.ContentTypeJSON)
				w.Header().Set("X-Content-Type-Options", "nosniff")
				statusErr := apierrors.NewServiceUnavailable("apiserver is shutting down").Status()
				w.WriteHeader(int(statusErr.Code))
				fmt.Fprintln(w, runtime.EncodeOrDie(scheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), &statusErr))
				return
			}
			defer wg.Done()
		}

		handler.ServeHTTP(w, req)
	})
}
<|endoftext|>


<commit_before>package v1

import (
	"database/sql"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"github.com/fritzpay/paymentd/pkg/paymentd/config"
	"github.com/fritzpay/paymentd/pkg/service"
	"github.com/fritzpay/paymentd/pkg/testutil"
	. "github.com/smartystreets/goconvey/convey"
	"gopkg.in/inconshreveable/log15.v2"
	"net/http"
	"testing"
)

func WithSystemPassword(db *sql.DB, f func()) func() {
	return func() {
		err := config.Set(db, config.SetPassword([]byte("password")))
		So(err, ShouldBeNil)

		Reset(func() {
			_, err := db.Exec(fmt.Sprintf("delete from config where name = '%s'", config.ConfigNameSystemPassword))
			So(err, ShouldBeNil)
		})

		f()
	}
}

func TestGetCredentialsWithBasicAuth(t *testing.T) {
	Convey("Given a new context", t, testutil.WithContext(func(ctx *service.Context, logChan <-chan *log15.Record) {
		ctx.Config().API.ServeAdmin = true
		So(ctx.Config().API.ServeAdmin, ShouldBeTrue)

		Convey("Given a new API service", WithService(ctx, logChan, func(s *Service, mux *http.ServeMux) {

			Convey("Given a new get credentials request", func() {
				r, err := http.NewRequest("GET", ServicePath+"/authorization", nil)
				So(err, ShouldBeNil)

				Convey("When the request method is PUT", func() {
					r.Method = "PUT"

					Convey("When the handler is called", func() {
						w := testutil.NewResponseWriter()
						mux.ServeHTTP(w, r)
						Convey("The handler should respond with method not allowed", func() {
							So(w.HeaderWritten, ShouldBeTrue)
							So(w.StatusCode, ShouldEqual, http.StatusMethodNotAllowed)
						})
					})
				})

				Convey("When the request method is DELETE", func() {
					r.Method = "DELETE"

					Convey("When the handler is called", func() {
						w := testutil.NewResponseWriter()
						mux.ServeHTTP(w, r)
						Convey("The handler should respond with method not allowed", func() {
							So(w.HeaderWritten, ShouldBeTrue)
							So(w.StatusCode, ShouldEqual, http.StatusMethodNotAllowed)
						})
					})
				})

				Convey("When the authentication method is unknown", func() {
					r.URL.Path += "/unknown"

					Convey("When the handler is called", func() {
						w := testutil.NewResponseWriter()
						mux.ServeHTTP(w, r)
						Convey("The handler should respond with a 404 (not found)", func() {
							So(w.HeaderWritten, ShouldBeTrue)
							So(w.StatusCode, ShouldEqual, http.StatusNotFound)
						})
					})
				})

				Convey("Given a payment DB", testutil.WithPaymentDB(t, func(db *sql.DB) {
					ctx.SetPaymentDB(db, nil)

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

					Convey("Given a set system password", WithSystemPassword(db, func() {
						Convey("When retrieving a basic authorization", func() {
							r.Method = "GET"
							r.URL.Path += "/basic"

							Convey("When using the correct password", func() {
								r.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("root:password")))

								Convey("When the handler is called", func() {
									w := testutil.NewResponseWriter()
									mux.ServeHTTP(w, r)
									Convey("The handler should respond with OK", func() {
										So(w.HeaderWritten, ShouldBeTrue)
										So(w.StatusCode, ShouldEqual, http.StatusOK)
										Convey("The body should contain the authorization container", func() {
											m := make(map[string]string)
											dec := json.NewDecoder(&w.Buf)
											err := dec.Decode(&m)
											So(err, ShouldBeNil)
											So(m["Authorization"], ShouldNotBeEmpty)
										})
									})

									Convey("Given the returned authorization container", func() {
										m := make(map[string]string)
										dec := json.NewDecoder(&w.Buf)
										err := dec.Decode(&m)
										So(err, ShouldBeNil)
										So(m["Authorization"], ShouldNotBeEmpty)

										Convey("Given a service request context", func() {
											service.SetRequestContext(r, ctx)

											Convey("Given a get user request", func() {
												r.Method = "GET"
												r.URL.Path = ServicePath + "/user"
												r.Header.Set("Authorization", m["Authorization"])

												Convey("When the handler is called", func() {
													w := testutil.NewResponseWriter()
													mux.ServeHTTP(w, r)
													Convey("The handler should respond with OK", func() {
														So(w.HeaderWritten, ShouldBeTrue)
														So(w.StatusCode, ShouldEqual, http.StatusOK)
													})
												})
											})
										})
									})
								})
							})

							Convey("When using a wrong password", func() {
								r.Header.Set("Authorization", "Basic dede")

								Convey("When the handler is called", func() {
									w := testutil.NewResponseWriter()
									mux.ServeHTTP(w, r)
									Convey("The handler should respond with Unauthorized", func() {
										So(w.HeaderWritten, ShouldBeTrue)
										So(w.StatusCode, ShouldEqual, http.StatusUnauthorized)
									})
								})
							})

							Convey("When using a bad authorization header", func() {
								r.Header.Set("Authorization", "Basic")

								Convey("When the handler is called", func() {
									w := testutil.NewResponseWriter()
									mux.ServeHTTP(w, r)
									Convey("The handler should request an authorization", func() {
										So(w.HeaderWritten, ShouldBeTrue)
										So(w.StatusCode, ShouldEqual, http.StatusUnauthorized)
										So(w.Header().Get("WWW-Authenticate"), ShouldNotBeEmpty)
									})
								})
							})
						})
					}))
				}))
			})
		}))
	}))
}
<commit_msg>allow parallel execution<commit_after>package v1

import (
	"database/sql"
	"encoding/base64"
	"encoding/json"
	"github.com/fritzpay/paymentd/pkg/paymentd/config"
	"github.com/fritzpay/paymentd/pkg/service"
	"github.com/fritzpay/paymentd/pkg/testutil"
	. "github.com/smartystreets/goconvey/convey"
	"gopkg.in/inconshreveable/log15.v2"
	"net/http"
	"testing"
)

func WithSystemPassword(db *sql.DB, f func()) func() {
	return func() {
		cfg := config.NewConfig()
		pw := config.SetPassword([]byte("password"))
		err := pw(cfg)
		So(err, ShouldBeNil)

		tx, err := db.Begin()
		So(err, ShouldBeNil)
		err = config.InsertConfigIfNotPresentTx(tx, cfg)
		So(err, ShouldBeNil)

		Reset(func() {
			err = tx.Rollback()
			So(err, ShouldBeNil)
		})

		f()
	}
}

func TestGetCredentialsWithBasicAuth(t *testing.T) {
	Convey("Given a new context", t, testutil.WithContext(func(ctx *service.Context, logChan <-chan *log15.Record) {
		ctx.Config().API.ServeAdmin = true
		So(ctx.Config().API.ServeAdmin, ShouldBeTrue)

		Convey("Given a new API service", WithService(ctx, logChan, func(s *Service, mux *http.ServeMux) {

			Convey("Given a new get credentials request", func() {
				r, err := http.NewRequest("GET", ServicePath+"/authorization", nil)
				So(err, ShouldBeNil)

				Convey("When the request method is PUT", func() {
					r.Method = "PUT"

					Convey("When the handler is called", func() {
						w := testutil.NewResponseWriter()
						mux.ServeHTTP(w, r)
						Convey("The handler should respond with method not allowed", func() {
							So(w.HeaderWritten, ShouldBeTrue)
							So(w.StatusCode, ShouldEqual, http.StatusMethodNotAllowed)
						})
					})
				})

				Convey("When the request method is DELETE", func() {
					r.Method = "DELETE"

					Convey("When the handler is called", func() {
						w := testutil.NewResponseWriter()
						mux.ServeHTTP(w, r)
						Convey("The handler should respond with method not allowed", func() {
							So(w.HeaderWritten, ShouldBeTrue)
							So(w.StatusCode, ShouldEqual, http.StatusMethodNotAllowed)
						})
					})
				})

				Convey("When the authentication method is unknown", func() {
					r.URL.Path += "/unknown"

					Convey("When the handler is called", func() {
						w := testutil.NewResponseWriter()
						mux.ServeHTTP(w, r)
						Convey("The handler should respond with a 404 (not found)", func() {
							So(w.HeaderWritten, ShouldBeTrue)
							So(w.StatusCode, ShouldEqual, http.StatusNotFound)
						})
					})
				})

				Convey("Given a payment DB", testutil.WithPaymentDB(t, func(db *sql.DB) {
					ctx.SetPaymentDB(db, nil)

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

					Convey("Given a set system password", WithSystemPassword(db, func() {
						Convey("When retrieving a basic authorization", func() {
							r.Method = "GET"
							r.URL.Path += "/basic"

							Convey("When using the correct password", func() {
								r.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("root:password")))

								Convey("When the handler is called", func() {
									w := testutil.NewResponseWriter()
									mux.ServeHTTP(w, r)
									Convey("The handler should respond with OK", func() {
										So(w.HeaderWritten, ShouldBeTrue)
										So(w.StatusCode, ShouldEqual, http.StatusOK)
										Convey("The body should contain the authorization container", func() {
											m := make(map[string]string)
											dec := json.NewDecoder(&w.Buf)
											err := dec.Decode(&m)
											So(err, ShouldBeNil)
											So(m["Authorization"], ShouldNotBeEmpty)
										})
									})

									Convey("Given the returned authorization container", func() {
										m := make(map[string]string)
										dec := json.NewDecoder(&w.Buf)
										err := dec.Decode(&m)
										So(err, ShouldBeNil)
										So(m["Authorization"], ShouldNotBeEmpty)

										Convey("Given a service request context", func() {
											service.SetRequestContext(r, ctx)

											Convey("Given a get user request", func() {
												r.Method = "GET"
												r.URL.Path = ServicePath + "/user"
												r.Header.Set("Authorization", m["Authorization"])

												Convey("When the handler is called", func() {
													w := testutil.NewResponseWriter()
													mux.ServeHTTP(w, r)
													Convey("The handler should respond with OK", func() {
														So(w.HeaderWritten, ShouldBeTrue)
														So(w.StatusCode, ShouldEqual, http.StatusOK)
													})
												})
											})
										})
									})
								})
							})

							Convey("When using a wrong password", func() {
								r.Header.Set("Authorization", "Basic dede")

								Convey("When the handler is called", func() {
									w := testutil.NewResponseWriter()
									mux.ServeHTTP(w, r)
									Convey("The handler should respond with Unauthorized", func() {
										So(w.HeaderWritten, ShouldBeTrue)
										So(w.StatusCode, ShouldEqual, http.StatusUnauthorized)
									})
								})
							})

							Convey("When using a bad authorization header", func() {
								r.Header.Set("Authorization", "Basic")

								Convey("When the handler is called", func() {
									w := testutil.NewResponseWriter()
									mux.ServeHTTP(w, r)
									Convey("The handler should request an authorization", func() {
										So(w.HeaderWritten, ShouldBeTrue)
										So(w.StatusCode, ShouldEqual, http.StatusUnauthorized)
										So(w.Header().Get("WWW-Authenticate"), ShouldNotBeEmpty)
									})
								})
							})
						})
					}))
				}))
			})
		}))
	}))
}
<|endoftext|>


<commit_before>package smokescreen

import (
	"fmt"
	"sort"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/stretchr/testify/require"
)

func TestMetricsTags(t *testing.T) {
	r := require.New(t)

	t.Run("add custom tags", func(t *testing.T) {
		metric := "acl.allow"
		mc := NewNoOpMetricsClient()

		err := mc.AddMetricTags(metric, []string{"globalize"})
		r.NoError(err)

		tags := mc.GetMetricTags(metric)
		r.Len(tags, 1)
		r.Equal(tags[0], "globalize")

		err = mc.AddMetricTags(metric, []string{"ignore"})
		r.NoError(err)

		tags = mc.GetMetricTags(metric)
		r.Len(tags, 2)
	})

	t.Run("add invalid tags", func(t *testing.T) {
		metric := "acl.does.not.exist"
		mc := NewNoOpMetricsClient()

		err := mc.AddMetricTags(metric, []string{"globalize"})
		r.Error(err)
	})
}

func TestMetricsClient(t *testing.T) {
	r := require.New(t)

	// Passing NewMetricsClient a missing statsd address should always fail
	t.Run("nil statsd addr", func(t *testing.T) {
		mc, err := NewMetricsClient("", "test_namespace")
		r.Error(err)
		r.Nil(mc)
	})

	// MetricsClient is not thread safe. Adding a tag after smokescreen has started
	// should always return an error.
	t.Run("adding metrics after started", func(t *testing.T) {
		mc := NewNoOpMetricsClient()
		mc.started.Store(true)

		err := mc.AddMetricTags("acl.allow", []string{"globalize"})
		r.Error(err)
	})
}

// MockMetricsClient is a MetricsClient that counts metric updates.
type MockMetricsClient struct {
	MetricsClient

	counts map[string]uint64
	mu     sync.Mutex
}

// NewMockMetricsClient returns a new MockMetricsClient that wraps a NoOpMetricsClient
// with counters to track metric updates.
func NewMockMetricsClient() *MockMetricsClient {
	return &MockMetricsClient{
		*NewNoOpMetricsClient(),
		make(map[string]uint64),
		sync.Mutex{},
	}
}

// countOne increments a metric count by 1, starting the count at 1 if the metric has
// not yet been counted. Call with m.mu.Lock held.
func (m *MockMetricsClient) countOne(metric string) {
	if i, ok := m.counts[metric]; ok {
		m.counts[metric] = i + 1
	} else {
		m.counts[metric] = 1
	}
}

func (m *MockMetricsClient) GetCount(metric string, tags ...string) (uint64, error) {
	m.mu.Lock()
	defer m.mu.Unlock()

	mName := metric
	sort.Strings(tags)
	if len(tags) > 0 {
		mName = fmt.Sprintf("%s %v", mName, tags)
	}
	i, ok := m.counts[mName]
	if !ok {
		keys := make([]string, len(m.counts))
		for k, _ := range m.counts {
			keys = append(keys, k)
		}
		return 0, fmt.Errorf("unknown metric %s (know %s)", mName, strings.Join(keys, ","))
	}

	return i, nil
}

func (m *MockMetricsClient) Incr(metric string, rate float64) error {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.countOne(metric)

	return m.MetricsClient.Incr(metric, rate)
}

func (m *MockMetricsClient) IncrWithTags(metric string, tags []string, rate float64) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	sort.Strings(tags)
	mName := fmt.Sprintf("%s %v", metric, tags)
	m.countOne(mName)

	return m.MetricsClient.IncrWithTags(metric, tags, rate)
}

func (m *MockMetricsClient) Timing(metric string, d time.Duration, rate float64) error {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.countOne(metric)

	return m.MetricsClient.Timing(metric, d, rate)
}

func (m *MockMetricsClient) TimingWithTags(metric string, d time.Duration, rate float64, tags []string) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	sort.Strings(tags)
	mName := fmt.Sprintf("%s %v", metric, tags)
	m.countOne(mName)

	return m.MetricsClient.TimingWithTags(metric, d, rate, tags)
}

var _ MetricsClientInterface = &MockMetricsClient{}
<commit_msg>add basic metrics tests<commit_after>package smokescreen

import (
	"fmt"
	"sort"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/stretchr/testify/require"
)

func TestMetricsTags(t *testing.T) {
	r := require.New(t)

	t.Run("add custom tags", func(t *testing.T) {
		metric := "acl.allow"
		mc := NewNoOpMetricsClient()

		err := mc.AddMetricTags(metric, []string{"globalize"})
		r.NoError(err)

		tags := mc.GetMetricTags(metric)
		r.Len(tags, 1)
		r.Equal(tags[0], "globalize")

		err = mc.AddMetricTags(metric, []string{"ignore"})
		r.NoError(err)

		tags = mc.GetMetricTags(metric)
		r.Len(tags, 2)
	})

	t.Run("add invalid tags", func(t *testing.T) {
		metric := "acl.does.not.exist"
		mc := NewNoOpMetricsClient()

		err := mc.AddMetricTags(metric, []string{"globalize"})
		r.Error(err)
	})
}

func TestMetricsClient(t *testing.T) {
	r := require.New(t)

	// Passing NewMetricsClient a missing statsd address should always fail
	t.Run("nil statsd addr", func(t *testing.T) {
		mc, err := NewMetricsClient("", "test_namespace")
		r.Error(err)
		r.Nil(mc)
	})

	// MetricsClient is not thread safe. Adding a tag after smokescreen has started
	// should always return an error.
	t.Run("adding metrics after started", func(t *testing.T) {
		mc := NewNoOpMetricsClient()
		mc.started.Store(true)

		err := mc.AddMetricTags("acl.allow", []string{"globalize"})
		r.Error(err)
	})
}

func TestMockMetricsClient(t *testing.T) {
	r := require.New(t)

	t.Run("Incr", func(t *testing.T) {
		m := NewMockMetricsClient()
		m.Incr("foobar", 1)
		c, err := m.GetCount("foobar")
		r.NoError(err)
		r.Equal(c, uint64(1))
	})

	t.Run("IncrWithTags", func(t *testing.T) {
		m := NewMockMetricsClient()
		tags := []string{"foo", "bar"}
		m.IncrWithTags("foobar", tags, 1)
		c, err := m.GetCount("foobar", tags...)
		r.NoError(err)
		r.Equal(c, uint64(1))
	})

	t.Run("IncrWithTags", func(t *testing.T) {
		m := NewMockMetricsClient()
		tags := []string{"foo", "bar"}
		m.IncrWithTags("foobar", tags, 1)
		c, err := m.GetCount("foobar", tags...)
		r.NoError(err)
		r.Equal(c, uint64(1))
	})

	t.Run("Timing", func(t *testing.T) {
		m := NewMockMetricsClient()
		m.Timing("foobar", time.Second, 1)
		c, err := m.GetCount("foobar")
		r.NoError(err)
		r.Equal(c, uint64(1))
	})

	t.Run("TimingWithTags", func(t *testing.T) {
		m := NewMockMetricsClient()
		tags := []string{"foo", "bar"}
		m.TimingWithTags("foobar", time.Second, 1, tags)
		c, err := m.GetCount("foobar", tags...)
		r.NoError(err)
		r.Equal(c, uint64(1))
	})
}

// MockMetricsClient is a MetricsClient that counts metric updates.
type MockMetricsClient struct {
	MetricsClient

	counts map[string]uint64
	mu     sync.Mutex
}

// NewMockMetricsClient returns a new MockMetricsClient that wraps a NoOpMetricsClient
// with counters to track metric updates.
func NewMockMetricsClient() *MockMetricsClient {
	return &MockMetricsClient{
		*NewNoOpMetricsClient(),
		make(map[string]uint64),
		sync.Mutex{},
	}
}

// countOne increments a metric count by 1, starting the count at 1 if the metric has
// not yet been counted. Call with m.mu.Lock held.
func (m *MockMetricsClient) countOne(metric string) {
	if i, ok := m.counts[metric]; ok {
		m.counts[metric] = i + 1
	} else {
		m.counts[metric] = 1
	}
}

func (m *MockMetricsClient) GetCount(metric string, tags ...string) (uint64, error) {
	m.mu.Lock()
	defer m.mu.Unlock()

	mName := metric
	sort.Strings(tags)
	if len(tags) > 0 {
		mName = fmt.Sprintf("%s %v", mName, tags)
	}
	i, ok := m.counts[mName]
	if !ok {
		keys := make([]string, len(m.counts))
		for k, _ := range m.counts {
			keys = append(keys, k)
		}
		return 0, fmt.Errorf("unknown metric %s (know %s)", mName, strings.Join(keys, ","))
	}

	return i, nil
}

func (m *MockMetricsClient) Incr(metric string, rate float64) error {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.countOne(metric)

	return m.MetricsClient.Incr(metric, rate)
}

func (m *MockMetricsClient) IncrWithTags(metric string, tags []string, rate float64) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	sort.Strings(tags)
	mName := fmt.Sprintf("%s %v", metric, tags)
	m.countOne(mName)

	return m.MetricsClient.IncrWithTags(metric, tags, rate)
}

func (m *MockMetricsClient) Timing(metric string, d time.Duration, rate float64) error {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.countOne(metric)

	return m.MetricsClient.Timing(metric, d, rate)
}

func (m *MockMetricsClient) TimingWithTags(metric string, d time.Duration, rate float64, tags []string) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	sort.Strings(tags)
	mName := fmt.Sprintf("%s %v", metric, tags)
	m.countOne(mName)

	return m.MetricsClient.TimingWithTags(metric, d, rate, tags)
}

var _ MetricsClientInterface = &MockMetricsClient{}
<|endoftext|>


<commit_before>/*
Copyright The Helm 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 driver // import "helm.sh/helm/v3/pkg/storage/driver"

import (
	"context"
	"fmt"
	"testing"

	v1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	kblabels "k8s.io/apimachinery/pkg/labels"
	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"

	rspb "helm.sh/helm/v3/pkg/release"
)

func releaseStub(name string, vers int, namespace string, status rspb.Status) *rspb.Release {
	return &rspb.Release{
		Name:      name,
		Version:   vers,
		Namespace: namespace,
		Info:      &rspb.Info{Status: status},
	}
}

func testKey(name string, vers int) string {
	return fmt.Sprintf("%s.v%d", name, vers)
}

func tsFixtureMemory(t *testing.T) *Memory {
	hs := []*rspb.Release{
		// rls-a
		releaseStub("rls-a", 4, "default", rspb.StatusDeployed),
		releaseStub("rls-a", 1, "default", rspb.StatusSuperseded),
		releaseStub("rls-a", 3, "default", rspb.StatusSuperseded),
		releaseStub("rls-a", 2, "default", rspb.StatusSuperseded),
		// rls-b
		releaseStub("rls-b", 4, "default", rspb.StatusDeployed),
		releaseStub("rls-b", 1, "default", rspb.StatusSuperseded),
		releaseStub("rls-b", 3, "default", rspb.StatusSuperseded),
		releaseStub("rls-b", 2, "default", rspb.StatusSuperseded),
		// rls-c in other namespace
		releaseStub("rls-c", 4, "mynamespace", rspb.StatusDeployed),
		releaseStub("rls-c", 1, "mynamespace", rspb.StatusSuperseded),
		releaseStub("rls-c", 3, "mynamespace", rspb.StatusSuperseded),
		releaseStub("rls-c", 2, "mynamespace", rspb.StatusSuperseded),
	}

	mem := NewMemory()
	for _, tt := range hs {
		err := mem.Create(testKey(tt.Name, tt.Version), tt)
		if err != nil {
			t.Fatalf("Test setup failed to create: %s\n", err)
		}
	}
	return mem
}

// newTestFixture initializes a MockConfigMapsInterface.
// ConfigMaps are created for each release provided.
func newTestFixtureCfgMaps(t *testing.T, releases ...*rspb.Release) *ConfigMaps {
	var mock MockConfigMapsInterface
	mock.Init(t, releases...)

	return NewConfigMaps(&mock)
}

// MockConfigMapsInterface mocks a kubernetes ConfigMapsInterface
type MockConfigMapsInterface struct {
	corev1.ConfigMapInterface

	objects map[string]*v1.ConfigMap
}

// Init initializes the MockConfigMapsInterface with the set of releases.
func (mock *MockConfigMapsInterface) Init(t *testing.T, releases ...*rspb.Release) {
	mock.objects = map[string]*v1.ConfigMap{}

	for _, rls := range releases {
		objkey := testKey(rls.Name, rls.Version)

		cfgmap, err := newConfigMapsObject(objkey, rls, nil)
		if err != nil {
			t.Fatalf("Failed to create configmap: %s", err)
		}
		mock.objects[objkey] = cfgmap
	}
}

// Get returns the ConfigMap by name.
func (mock *MockConfigMapsInterface) Get(_ context.Context, name string, _ metav1.GetOptions) (*v1.ConfigMap, error) {
	object, ok := mock.objects[name]
	if !ok {
		return nil, apierrors.NewNotFound(v1.Resource("tests"), name)
	}
	return object, nil
}

// List returns the a of ConfigMaps.
func (mock *MockConfigMapsInterface) List(_ context.Context, _ metav1.ListOptions) (*v1.ConfigMapList, error) {
	var list v1.ConfigMapList

	labelSelector, err := kblabels.Parse(opts.LabelSelector)
	if err != nil {
		return nil, err
	}

	for _, cfgmap := range mock.objects {
		if labelSelector.Matches(kblabels.Set(cfgmap.ObjectMeta.Labels)) {
			list.Items = append(list.Items, *cfgmap)
		}
	}
	return &list, nil
}

// Create creates a new ConfigMap.
func (mock *MockConfigMapsInterface) Create(_ context.Context, cfgmap *v1.ConfigMap, _ metav1.CreateOptions) (*v1.ConfigMap, error) {
	name := cfgmap.ObjectMeta.Name
	if object, ok := mock.objects[name]; ok {
		return object, apierrors.NewAlreadyExists(v1.Resource("tests"), name)
	}
	mock.objects[name] = cfgmap
	return cfgmap, nil
}

// Update updates a ConfigMap.
func (mock *MockConfigMapsInterface) Update(_ context.Context, cfgmap *v1.ConfigMap, _ metav1.UpdateOptions) (*v1.ConfigMap, error) {
	name := cfgmap.ObjectMeta.Name
	if _, ok := mock.objects[name]; !ok {
		return nil, apierrors.NewNotFound(v1.Resource("tests"), name)
	}
	mock.objects[name] = cfgmap
	return cfgmap, nil
}

// Delete deletes a ConfigMap by name.
func (mock *MockConfigMapsInterface) Delete(_ context.Context, name string, _ metav1.DeleteOptions) error {
	if _, ok := mock.objects[name]; !ok {
		return apierrors.NewNotFound(v1.Resource("tests"), name)
	}
	delete(mock.objects, name)
	return nil
}

// newTestFixture initializes a MockSecretsInterface.
// Secrets are created for each release provided.
func newTestFixtureSecrets(t *testing.T, releases ...*rspb.Release) *Secrets {
	var mock MockSecretsInterface
	mock.Init(t, releases...)

	return NewSecrets(&mock)
}

// MockSecretsInterface mocks a kubernetes SecretsInterface
type MockSecretsInterface struct {
	corev1.SecretInterface

	objects map[string]*v1.Secret
}

// Init initializes the MockSecretsInterface with the set of releases.
func (mock *MockSecretsInterface) Init(t *testing.T, releases ...*rspb.Release) {
	mock.objects = map[string]*v1.Secret{}

	for _, rls := range releases {
		objkey := testKey(rls.Name, rls.Version)

		secret, err := newSecretsObject(objkey, rls, nil)
		if err != nil {
			t.Fatalf("Failed to create secret: %s", err)
		}
		mock.objects[objkey] = secret
	}
}

// Get returns the Secret by name.
func (mock *MockSecretsInterface) Get(_ context.Context, name string, _ metav1.GetOptions) (*v1.Secret, error) {
	object, ok := mock.objects[name]
	if !ok {
		return nil, apierrors.NewNotFound(v1.Resource("tests"), name)
	}
	return object, nil
}

// List returns the a of Secret.
func (mock *MockSecretsInterface) List(_ context.Context, _ metav1.ListOptions) (*v1.SecretList, error) {
	var list v1.SecretList

	labelSelector, err := kblabels.Parse(opts.LabelSelector)
	if err != nil {
		return nil, err
	}

	for _, secret := range mock.objects {
		if labelSelector.Matches(kblabels.Set(secret.ObjectMeta.Labels)) {
			list.Items = append(list.Items, *secret)
		}
	}
	return &list, nil
}

// Create creates a new Secret.
func (mock *MockSecretsInterface) Create(_ context.Context, secret *v1.Secret, _ metav1.CreateOptions) (*v1.Secret, error) {
	name := secret.ObjectMeta.Name
	if object, ok := mock.objects[name]; ok {
		return object, apierrors.NewAlreadyExists(v1.Resource("tests"), name)
	}
	mock.objects[name] = secret
	return secret, nil
}

// Update updates a Secret.
func (mock *MockSecretsInterface) Update(_ context.Context, secret *v1.Secret, _ metav1.UpdateOptions) (*v1.Secret, error) {
	name := secret.ObjectMeta.Name
	if _, ok := mock.objects[name]; !ok {
		return nil, apierrors.NewNotFound(v1.Resource("tests"), name)
	}
	mock.objects[name] = secret
	return secret, nil
}

// Delete deletes a Secret by name.
func (mock *MockSecretsInterface) Delete(_ context.Context, name string, _ metav1.DeleteOptions) error {
	if _, ok := mock.objects[name]; !ok {
		return apierrors.NewNotFound(v1.Resource("tests"), name)
	}
	delete(mock.objects, name)
	return nil
}
<commit_msg>fix(tests): fix broken unit tests in storage (#7928)<commit_after>/*
Copyright The Helm 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 driver // import "helm.sh/helm/v3/pkg/storage/driver"

import (
	"context"
	"fmt"
	"testing"

	v1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	kblabels "k8s.io/apimachinery/pkg/labels"
	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"

	rspb "helm.sh/helm/v3/pkg/release"
)

func releaseStub(name string, vers int, namespace string, status rspb.Status) *rspb.Release {
	return &rspb.Release{
		Name:      name,
		Version:   vers,
		Namespace: namespace,
		Info:      &rspb.Info{Status: status},
	}
}

func testKey(name string, vers int) string {
	return fmt.Sprintf("%s.v%d", name, vers)
}

func tsFixtureMemory(t *testing.T) *Memory {
	hs := []*rspb.Release{
		// rls-a
		releaseStub("rls-a", 4, "default", rspb.StatusDeployed),
		releaseStub("rls-a", 1, "default", rspb.StatusSuperseded),
		releaseStub("rls-a", 3, "default", rspb.StatusSuperseded),
		releaseStub("rls-a", 2, "default", rspb.StatusSuperseded),
		// rls-b
		releaseStub("rls-b", 4, "default", rspb.StatusDeployed),
		releaseStub("rls-b", 1, "default", rspb.StatusSuperseded),
		releaseStub("rls-b", 3, "default", rspb.StatusSuperseded),
		releaseStub("rls-b", 2, "default", rspb.StatusSuperseded),
		// rls-c in other namespace
		releaseStub("rls-c", 4, "mynamespace", rspb.StatusDeployed),
		releaseStub("rls-c", 1, "mynamespace", rspb.StatusSuperseded),
		releaseStub("rls-c", 3, "mynamespace", rspb.StatusSuperseded),
		releaseStub("rls-c", 2, "mynamespace", rspb.StatusSuperseded),
	}

	mem := NewMemory()
	for _, tt := range hs {
		err := mem.Create(testKey(tt.Name, tt.Version), tt)
		if err != nil {
			t.Fatalf("Test setup failed to create: %s\n", err)
		}
	}
	return mem
}

// newTestFixture initializes a MockConfigMapsInterface.
// ConfigMaps are created for each release provided.
func newTestFixtureCfgMaps(t *testing.T, releases ...*rspb.Release) *ConfigMaps {
	var mock MockConfigMapsInterface
	mock.Init(t, releases...)

	return NewConfigMaps(&mock)
}

// MockConfigMapsInterface mocks a kubernetes ConfigMapsInterface
type MockConfigMapsInterface struct {
	corev1.ConfigMapInterface

	objects map[string]*v1.ConfigMap
}

// Init initializes the MockConfigMapsInterface with the set of releases.
func (mock *MockConfigMapsInterface) Init(t *testing.T, releases ...*rspb.Release) {
	mock.objects = map[string]*v1.ConfigMap{}

	for _, rls := range releases {
		objkey := testKey(rls.Name, rls.Version)

		cfgmap, err := newConfigMapsObject(objkey, rls, nil)
		if err != nil {
			t.Fatalf("Failed to create configmap: %s", err)
		}
		mock.objects[objkey] = cfgmap
	}
}

// Get returns the ConfigMap by name.
func (mock *MockConfigMapsInterface) Get(_ context.Context, name string, _ metav1.GetOptions) (*v1.ConfigMap, error) {
	object, ok := mock.objects[name]
	if !ok {
		return nil, apierrors.NewNotFound(v1.Resource("tests"), name)
	}
	return object, nil
}

// List returns the a of ConfigMaps.
func (mock *MockConfigMapsInterface) List(_ context.Context, opts metav1.ListOptions) (*v1.ConfigMapList, error) {
	var list v1.ConfigMapList

	labelSelector, err := kblabels.Parse(opts.LabelSelector)
	if err != nil {
		return nil, err
	}

	for _, cfgmap := range mock.objects {
		if labelSelector.Matches(kblabels.Set(cfgmap.ObjectMeta.Labels)) {
			list.Items = append(list.Items, *cfgmap)
		}
	}
	return &list, nil
}

// Create creates a new ConfigMap.
func (mock *MockConfigMapsInterface) Create(_ context.Context, cfgmap *v1.ConfigMap, _ metav1.CreateOptions) (*v1.ConfigMap, error) {
	name := cfgmap.ObjectMeta.Name
	if object, ok := mock.objects[name]; ok {
		return object, apierrors.NewAlreadyExists(v1.Resource("tests"), name)
	}
	mock.objects[name] = cfgmap
	return cfgmap, nil
}

// Update updates a ConfigMap.
func (mock *MockConfigMapsInterface) Update(_ context.Context, cfgmap *v1.ConfigMap, _ metav1.UpdateOptions) (*v1.ConfigMap, error) {
	name := cfgmap.ObjectMeta.Name
	if _, ok := mock.objects[name]; !ok {
		return nil, apierrors.NewNotFound(v1.Resource("tests"), name)
	}
	mock.objects[name] = cfgmap
	return cfgmap, nil
}

// Delete deletes a ConfigMap by name.
func (mock *MockConfigMapsInterface) Delete(_ context.Context, name string, _ metav1.DeleteOptions) error {
	if _, ok := mock.objects[name]; !ok {
		return apierrors.NewNotFound(v1.Resource("tests"), name)
	}
	delete(mock.objects, name)
	return nil
}

// newTestFixture initializes a MockSecretsInterface.
// Secrets are created for each release provided.
func newTestFixtureSecrets(t *testing.T, releases ...*rspb.Release) *Secrets {
	var mock MockSecretsInterface
	mock.Init(t, releases...)

	return NewSecrets(&mock)
}

// MockSecretsInterface mocks a kubernetes SecretsInterface
type MockSecretsInterface struct {
	corev1.SecretInterface

	objects map[string]*v1.Secret
}

// Init initializes the MockSecretsInterface with the set of releases.
func (mock *MockSecretsInterface) Init(t *testing.T, releases ...*rspb.Release) {
	mock.objects = map[string]*v1.Secret{}

	for _, rls := range releases {
		objkey := testKey(rls.Name, rls.Version)

		secret, err := newSecretsObject(objkey, rls, nil)
		if err != nil {
			t.Fatalf("Failed to create secret: %s", err)
		}
		mock.objects[objkey] = secret
	}
}

// Get returns the Secret by name.
func (mock *MockSecretsInterface) Get(_ context.Context, name string, _ metav1.GetOptions) (*v1.Secret, error) {
	object, ok := mock.objects[name]
	if !ok {
		return nil, apierrors.NewNotFound(v1.Resource("tests"), name)
	}
	return object, nil
}

// List returns the a of Secret.
func (mock *MockSecretsInterface) List(_ context.Context, opts metav1.ListOptions) (*v1.SecretList, error) {
	var list v1.SecretList

	labelSelector, err := kblabels.Parse(opts.LabelSelector)
	if err != nil {
		return nil, err
	}

	for _, secret := range mock.objects {
		if labelSelector.Matches(kblabels.Set(secret.ObjectMeta.Labels)) {
			list.Items = append(list.Items, *secret)
		}
	}
	return &list, nil
}

// Create creates a new Secret.
func (mock *MockSecretsInterface) Create(_ context.Context, secret *v1.Secret, _ metav1.CreateOptions) (*v1.Secret, error) {
	name := secret.ObjectMeta.Name
	if object, ok := mock.objects[name]; ok {
		return object, apierrors.NewAlreadyExists(v1.Resource("tests"), name)
	}
	mock.objects[name] = secret
	return secret, nil
}

// Update updates a Secret.
func (mock *MockSecretsInterface) Update(_ context.Context, secret *v1.Secret, _ metav1.UpdateOptions) (*v1.Secret, error) {
	name := secret.ObjectMeta.Name
	if _, ok := mock.objects[name]; !ok {
		return nil, apierrors.NewNotFound(v1.Resource("tests"), name)
	}
	mock.objects[name] = secret
	return secret, nil
}

// Delete deletes a Secret by name.
func (mock *MockSecretsInterface) Delete(_ context.Context, name string, _ metav1.DeleteOptions) error {
	if _, ok := mock.objects[name]; !ok {
		return apierrors.NewNotFound(v1.Resource("tests"), name)
	}
	delete(mock.objects, name)
	return nil
}
<|endoftext|>


<commit_before>package ginkgo

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/signal"
	"path/filepath"
	"sort"
	"strings"
	"syscall"
	"time"

	"github.com/openshift/origin/pkg/monitor"

	"github.com/onsi/ginkgo/config"
)

// Options is used to run a suite of tests by invoking each test
// as a call to a child worker (the run-tests command).
type Options struct {
	Parallelism  int
	Timeout      time.Duration
	JUnitDir     string
	TestFile     string
	OutFile      string
	DetectFlakes int

	IncludeSuccessOutput bool

	Provider string

	Suites []*TestSuite

	DryRun      bool
	Out, ErrOut io.Writer
}

func (opt *Options) Run(args []string) error {
	var suite *TestSuite

	if len(opt.TestFile) > 0 {
		var in []byte
		var err error
		if opt.TestFile == "-" {
			in, err = ioutil.ReadAll(os.Stdin)
			if err != nil {
				return err
			}
		} else {
			in, err = ioutil.ReadFile(opt.TestFile)
		}
		if err != nil {
			return err
		}
		suite, err = newSuiteFromFile("files", in)
		if err != nil {
			return fmt.Errorf("could not read test suite from input: %v", err)
		}
	}

	if suite == nil && len(args) == 0 {
		fmt.Fprintf(opt.ErrOut, SuitesString(opt.Suites, "Select a test suite to run against the server:\n\n"))
		return fmt.Errorf("specify a test suite to run, for example: %s run %s", filepath.Base(os.Args[0]), opt.Suites[0].Name)
	}
	if suite == nil && len(args) > 0 {
		for _, s := range opt.Suites {
			if s.Name == args[0] {
				suite = s
				break
			}
		}
	}
	if suite == nil {
		fmt.Fprintf(opt.ErrOut, SuitesString(opt.Suites, "Select a test suite to run against the server:\n\n"))
		return fmt.Errorf("suite %q does not exist", args[0])
	}

	tests, err := testsForSuite(config.GinkgoConfig)
	if err != nil {
		return err
	}

	// This ensures that tests in the identified paths do not run in parallel, because
	// the test suite reuses shared resources without considering whether another test
	// could be running at the same time. While these are technically [Serial], ginkgo
	// parallel mode provides this guarantee. Doing this for all suites would be too
	// slow.
	setTestExclusion(tests, func(suitePath string, t *testCase) bool {
		for _, name := range []string{
			"/k8s.io/kubernetes/test/e2e/apps/disruption.go",
		} {
			if strings.HasSuffix(suitePath, name) {
				return true
			}
		}
		return false
	})

	tests = suite.Filter(tests)
	if len(tests) == 0 {
		return fmt.Errorf("suite %q does not contain any tests", suite.Name)
	}

	if opt.DryRun {
		// if true {
		// 	bySuite := make(map[string][]*testCase)
		// 	for _, test := range tests {
		// 		bySuite[test.testExclusion] = append(bySuite[test.testExclusion], test)
		// 	}
		// 	var names []string
		// 	for k := range bySuite {
		// 		names = append(names, k)
		// 	}
		// 	sort.Slice(names, func(i, j int) bool {
		// 		return len(bySuite[names[i]]) > len(bySuite[names[j]])
		// 	})
		// 	for _, name := range names {
		// 		if len(name) == 0 {
		// 			fmt.Fprintf(out, "<none>:\n")
		// 		} else {
		// 			fmt.Fprintf(out, "%s:\n", name)
		// 		}
		// 		for _, test := range sortedTests(bySuite[name]) {
		// 			fmt.Fprintf(out, "  %q\n", test.name)
		// 		}
		// 	}
		// 	return nil
		// }
		for _, test := range sortedTests(tests) {
			fmt.Fprintf(opt.Out, "%q\n", test.name)
		}
		return nil
	}

	if len(opt.JUnitDir) > 0 {
		if _, err := os.Stat(opt.JUnitDir); err != nil {
			if !os.IsNotExist(err) {
				return fmt.Errorf("could not access --junit-dir: %v", err)
			}
			if err := os.MkdirAll(opt.JUnitDir, 0755); err != nil {
				return fmt.Errorf("could not create --junit-dir: %v", err)
			}
		}
	}

	parallelism := opt.Parallelism
	if parallelism == 0 {
		parallelism = suite.Parallelism
	}
	if parallelism == 0 {
		parallelism = 10
	}
	timeout := opt.Timeout
	if timeout == 0 {
		timeout = suite.TestTimeout
	}
	if timeout == 0 {
		// TODO: temporarily increased because some normally fast build tests have become much slower
		//   reduce back to 10m at some point in the future
		timeout = 15 * time.Minute
	}

	ctx, cancelFn := context.WithCancel(context.Background())
	defer cancelFn()
	abortCh := make(chan os.Signal)
	go func() {
		<-abortCh
		fmt.Fprintf(opt.ErrOut, "Interrupted, terminating tests\n")
		cancelFn()
		sig := <-abortCh
		fmt.Fprintf(opt.ErrOut, "Interrupted twice, exiting (%s)\n", sig)
		switch sig {
		case syscall.SIGINT:
			os.Exit(130)
		default:
			os.Exit(0)
		}
	}()
	signal.Notify(abortCh, syscall.SIGINT, syscall.SIGTERM)

	m, err := monitor.Start(ctx)
	if err != nil {
		return err
	}
	status := newTestStatus(opt.Out, opt.IncludeSuccessOutput, len(tests), timeout, m)

	smoke, normal := splitTests(tests, func(t *testCase) bool {
		return strings.Contains(t.name, "[Smoke]")
	})

	// run the tests
	start := time.Now()

	// run our smoke tests first
	q := newParallelTestQueue(smoke)
	q.Execute(ctx, parallelism, status.Run)

	// run other tests next
	q = newParallelTestQueue(normal)
	q.Execute(ctx, parallelism, status.Run)

	duration := time.Now().Sub(start).Round(time.Second / 10)
	if duration > time.Minute {
		duration = duration.Round(time.Second)
	}

	pass, fail, skip, failing := summarizeTests(tests)

	// monitor the cluster while the tests are running and report any detected
	// anomalies
	var syntheticTestResults []*JUnitTestCase
	if events := m.Events(time.Time{}, time.Time{}); len(events) > 0 {
		buf, errBuf := &bytes.Buffer{}, &bytes.Buffer{}
		fmt.Fprintf(buf, "\nTimeline:\n\n")
		errorCount := 0
		for _, test := range tests {
			if !test.failed {
				continue
			}
			events = append(events,
				&monitor.EventInterval{
					From: test.start,
					To:   test.end,
					Condition: &monitor.Condition{
						Level:   monitor.Info,
						Locator: fmt.Sprintf("test=%q", test.name),
						Message: "running",
					},
				},
				&monitor.EventInterval{
					From: test.end,
					To:   test.end,
					Condition: &monitor.Condition{
						Level:   monitor.Info,
						Locator: fmt.Sprintf("test=%q", test.name),
						Message: "failed",
					},
				},
			)
		}
		sort.Sort(events)
		for _, event := range events {
			if event.Level == monitor.Error {
				errorCount++
				fmt.Fprintln(errBuf, event.String())
			}
			fmt.Fprintln(buf, event.String())
		}
		fmt.Fprintln(buf)

		if errorCount > 0 {
			syntheticTestResults = append(syntheticTestResults, &JUnitTestCase{
				Name:      "Monitor cluster while tests execute",
				SystemOut: buf.String(),
				Duration:  duration.Seconds(),
				FailureOutput: &FailureOutput{
					Output: fmt.Sprintf("%d error level events were detected during this test run:\n\n%s", errorCount, errBuf.String()),
				},
			})
		}

		opt.Out.Write(buf.Bytes())
	}

	// attempt to retry failures to do flake detection
	if fail > 0 && fail <= opt.DetectFlakes {
		var retries []*testCase
		for _, test := range failing {
			retries = append(retries, test.Retry())
			if len(retries) > opt.DetectFlakes {
				break
			}
		}

		q := newParallelTestQueue(retries)
		status := newTestStatus(ioutil.Discard, opt.IncludeSuccessOutput, len(retries), timeout, m)
		q.Execute(ctx, parallelism, status.Run)
		var flaky []string
		var repeatFailures []*testCase
		for _, test := range retries {
			if test.success {
				flaky = append(flaky, test.name)
			} else {
				repeatFailures = append(repeatFailures, test)
			}
		}
		if len(flaky) > 0 {
			failing = repeatFailures
			sort.Strings(flaky)
			fmt.Fprintf(opt.Out, "Flaky tests:\n\n%s\n\n", strings.Join(flaky, "\n"))
		}
	}

	if len(failing) > 0 {
		names := testNames(failing)
		sort.Strings(names)
		fmt.Fprintf(opt.Out, "Failing tests:\n\n%s\n\n", strings.Join(names, "\n"))
	}

	if len(opt.JUnitDir) > 0 {
		if err := writeJUnitReport("openshift-tests", tests, opt.JUnitDir, duration, opt.ErrOut, syntheticTestResults...); err != nil {
			fmt.Fprintf(opt.Out, "error: Unable to write JUnit results: %v", err)
		}
	}

	if fail > 0 {
		if len(failing) > 0 || !suite.AllowPassWithFlakes {
			return fmt.Errorf("%d fail, %d pass, %d skip (%s)", fail, pass, skip, duration)
		}
		fmt.Fprintf(opt.Out, "%d flakes detected, suite allows passing with only flakes\n\n", fail)
	}

	// if we detect a transition that is considered an error state (invariant violation) during
	// the test run, exit with a failure
	if len(syntheticTestResults) > 0 {
		return fmt.Errorf("failing due to error event detection, %d fail, %d pass, %d skip (%s)", fail, pass, skip, duration)
	}

	fmt.Fprintf(opt.Out, "%d pass, %d skip (%s)\n", pass, skip, duration)
	return nil
}
<commit_msg>Don't error out for now when events are detected<commit_after>package ginkgo

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/signal"
	"path/filepath"
	"sort"
	"strings"
	"syscall"
	"time"

	"github.com/openshift/origin/pkg/monitor"

	"github.com/onsi/ginkgo/config"
)

// Options is used to run a suite of tests by invoking each test
// as a call to a child worker (the run-tests command).
type Options struct {
	Parallelism  int
	Timeout      time.Duration
	JUnitDir     string
	TestFile     string
	OutFile      string
	DetectFlakes int

	IncludeSuccessOutput bool

	Provider string

	Suites []*TestSuite

	DryRun      bool
	Out, ErrOut io.Writer
}

func (opt *Options) Run(args []string) error {
	var suite *TestSuite

	if len(opt.TestFile) > 0 {
		var in []byte
		var err error
		if opt.TestFile == "-" {
			in, err = ioutil.ReadAll(os.Stdin)
			if err != nil {
				return err
			}
		} else {
			in, err = ioutil.ReadFile(opt.TestFile)
		}
		if err != nil {
			return err
		}
		suite, err = newSuiteFromFile("files", in)
		if err != nil {
			return fmt.Errorf("could not read test suite from input: %v", err)
		}
	}

	if suite == nil && len(args) == 0 {
		fmt.Fprintf(opt.ErrOut, SuitesString(opt.Suites, "Select a test suite to run against the server:\n\n"))
		return fmt.Errorf("specify a test suite to run, for example: %s run %s", filepath.Base(os.Args[0]), opt.Suites[0].Name)
	}
	if suite == nil && len(args) > 0 {
		for _, s := range opt.Suites {
			if s.Name == args[0] {
				suite = s
				break
			}
		}
	}
	if suite == nil {
		fmt.Fprintf(opt.ErrOut, SuitesString(opt.Suites, "Select a test suite to run against the server:\n\n"))
		return fmt.Errorf("suite %q does not exist", args[0])
	}

	tests, err := testsForSuite(config.GinkgoConfig)
	if err != nil {
		return err
	}

	// This ensures that tests in the identified paths do not run in parallel, because
	// the test suite reuses shared resources without considering whether another test
	// could be running at the same time. While these are technically [Serial], ginkgo
	// parallel mode provides this guarantee. Doing this for all suites would be too
	// slow.
	setTestExclusion(tests, func(suitePath string, t *testCase) bool {
		for _, name := range []string{
			"/k8s.io/kubernetes/test/e2e/apps/disruption.go",
		} {
			if strings.HasSuffix(suitePath, name) {
				return true
			}
		}
		return false
	})

	tests = suite.Filter(tests)
	if len(tests) == 0 {
		return fmt.Errorf("suite %q does not contain any tests", suite.Name)
	}

	if opt.DryRun {
		// if true {
		// 	bySuite := make(map[string][]*testCase)
		// 	for _, test := range tests {
		// 		bySuite[test.testExclusion] = append(bySuite[test.testExclusion], test)
		// 	}
		// 	var names []string
		// 	for k := range bySuite {
		// 		names = append(names, k)
		// 	}
		// 	sort.Slice(names, func(i, j int) bool {
		// 		return len(bySuite[names[i]]) > len(bySuite[names[j]])
		// 	})
		// 	for _, name := range names {
		// 		if len(name) == 0 {
		// 			fmt.Fprintf(out, "<none>:\n")
		// 		} else {
		// 			fmt.Fprintf(out, "%s:\n", name)
		// 		}
		// 		for _, test := range sortedTests(bySuite[name]) {
		// 			fmt.Fprintf(out, "  %q\n", test.name)
		// 		}
		// 	}
		// 	return nil
		// }
		for _, test := range sortedTests(tests) {
			fmt.Fprintf(opt.Out, "%q\n", test.name)
		}
		return nil
	}

	if len(opt.JUnitDir) > 0 {
		if _, err := os.Stat(opt.JUnitDir); err != nil {
			if !os.IsNotExist(err) {
				return fmt.Errorf("could not access --junit-dir: %v", err)
			}
			if err := os.MkdirAll(opt.JUnitDir, 0755); err != nil {
				return fmt.Errorf("could not create --junit-dir: %v", err)
			}
		}
	}

	parallelism := opt.Parallelism
	if parallelism == 0 {
		parallelism = suite.Parallelism
	}
	if parallelism == 0 {
		parallelism = 10
	}
	timeout := opt.Timeout
	if timeout == 0 {
		timeout = suite.TestTimeout
	}
	if timeout == 0 {
		// TODO: temporarily increased because some normally fast build tests have become much slower
		//   reduce back to 10m at some point in the future
		timeout = 15 * time.Minute
	}

	ctx, cancelFn := context.WithCancel(context.Background())
	defer cancelFn()
	abortCh := make(chan os.Signal)
	go func() {
		<-abortCh
		fmt.Fprintf(opt.ErrOut, "Interrupted, terminating tests\n")
		cancelFn()
		sig := <-abortCh
		fmt.Fprintf(opt.ErrOut, "Interrupted twice, exiting (%s)\n", sig)
		switch sig {
		case syscall.SIGINT:
			os.Exit(130)
		default:
			os.Exit(0)
		}
	}()
	signal.Notify(abortCh, syscall.SIGINT, syscall.SIGTERM)

	m, err := monitor.Start(ctx)
	if err != nil {
		return err
	}
	status := newTestStatus(opt.Out, opt.IncludeSuccessOutput, len(tests), timeout, m)

	smoke, normal := splitTests(tests, func(t *testCase) bool {
		return strings.Contains(t.name, "[Smoke]")
	})

	// run the tests
	start := time.Now()

	// run our smoke tests first
	q := newParallelTestQueue(smoke)
	q.Execute(ctx, parallelism, status.Run)

	// run other tests next
	q = newParallelTestQueue(normal)
	q.Execute(ctx, parallelism, status.Run)

	duration := time.Now().Sub(start).Round(time.Second / 10)
	if duration > time.Minute {
		duration = duration.Round(time.Second)
	}

	pass, fail, skip, failing := summarizeTests(tests)

	// monitor the cluster while the tests are running and report any detected
	// anomalies
	var syntheticTestResults []*JUnitTestCase
	if events := m.Events(time.Time{}, time.Time{}); len(events) > 0 {
		buf, errBuf := &bytes.Buffer{}, &bytes.Buffer{}
		fmt.Fprintf(buf, "\nTimeline:\n\n")
		errorCount := 0
		for _, test := range tests {
			if !test.failed {
				continue
			}
			events = append(events,
				&monitor.EventInterval{
					From: test.start,
					To:   test.end,
					Condition: &monitor.Condition{
						Level:   monitor.Info,
						Locator: fmt.Sprintf("test=%q", test.name),
						Message: "running",
					},
				},
				&monitor.EventInterval{
					From: test.end,
					To:   test.end,
					Condition: &monitor.Condition{
						Level:   monitor.Info,
						Locator: fmt.Sprintf("test=%q", test.name),
						Message: "failed",
					},
				},
			)
		}
		sort.Sort(events)
		for _, event := range events {
			if event.Level == monitor.Error {
				errorCount++
				fmt.Fprintln(errBuf, event.String())
			}
			fmt.Fprintln(buf, event.String())
		}
		fmt.Fprintln(buf)

		if errorCount > 0 {
			syntheticTestResults = append(syntheticTestResults, &JUnitTestCase{
				Name:      "Monitor cluster while tests execute",
				SystemOut: buf.String(),
				Duration:  duration.Seconds(),
				FailureOutput: &FailureOutput{
					Output: fmt.Sprintf("%d error level events were detected during this test run:\n\n%s", errorCount, errBuf.String()),
				},
			})
		}

		opt.Out.Write(buf.Bytes())
	}

	// attempt to retry failures to do flake detection
	if fail > 0 && fail <= opt.DetectFlakes {
		var retries []*testCase
		for _, test := range failing {
			retries = append(retries, test.Retry())
			if len(retries) > opt.DetectFlakes {
				break
			}
		}

		q := newParallelTestQueue(retries)
		status := newTestStatus(ioutil.Discard, opt.IncludeSuccessOutput, len(retries), timeout, m)
		q.Execute(ctx, parallelism, status.Run)
		var flaky []string
		var repeatFailures []*testCase
		for _, test := range retries {
			if test.success {
				flaky = append(flaky, test.name)
			} else {
				repeatFailures = append(repeatFailures, test)
			}
		}
		if len(flaky) > 0 {
			failing = repeatFailures
			sort.Strings(flaky)
			fmt.Fprintf(opt.Out, "Flaky tests:\n\n%s\n\n", strings.Join(flaky, "\n"))
		}
	}

	if len(failing) > 0 {
		names := testNames(failing)
		sort.Strings(names)
		fmt.Fprintf(opt.Out, "Failing tests:\n\n%s\n\n", strings.Join(names, "\n"))
	}

	if len(opt.JUnitDir) > 0 {
		if err := writeJUnitReport("openshift-tests", tests, opt.JUnitDir, duration, opt.ErrOut, syntheticTestResults...); err != nil {
			fmt.Fprintf(opt.Out, "error: Unable to write JUnit results: %v", err)
		}
	}

	if fail > 0 {
		if len(failing) > 0 || !suite.AllowPassWithFlakes {
			return fmt.Errorf("%d fail, %d pass, %d skip (%s)", fail, pass, skip, duration)
		}
		fmt.Fprintf(opt.Out, "%d flakes detected, suite allows passing with only flakes\n\n", fail)
	}

	fmt.Fprintf(opt.Out, "%d pass, %d skip (%s)\n", pass, skip, duration)
	return nil
}
<|endoftext|>