Rework state tests to avoid boilerplate. Begin adding pod tests.

Signed-off-by: Matthew Heon <matthew.heon@gmail.com>

Closes: #268
Approved by: rhatdan
This commit is contained in:
Matthew Heon
2018-01-29 04:59:25 -05:00
committed by Atomic Bot
parent 0920e8de5a
commit 4ecebf20b4
7 changed files with 759 additions and 385 deletions

View File

@ -8,9 +8,9 @@ import (
"github.com/projectatomic/libpod/pkg/registrar"
)
// TODO: unified name/ID registry to ensure no name and ID conflicts between
// containers and pods
// This can probably be used to replace the existing trunc index and registrars
// TODO: Maybe separate idIndex for pod/containers
// As of right now, partial IDs used in Lookup... need to be unique as well
// This may be undesirable?
// An InMemoryState is a purely in-memory state store
type InMemoryState struct {
@ -486,7 +486,7 @@ func (s *InMemoryState) AddContainerToPod(pod *Pod, ctr *Container) error {
}
// Is the container already in the pod?
if _, ok := podCtrs[ctr.ID()]; ok {
if _, ok = podCtrs[ctr.ID()]; ok {
return errors.Wrapf(ErrCtrExists, "container with ID %s already exists in pod %s", ctr.ID(), pod.ID())
}

View File

@ -59,7 +59,7 @@ func newPod(lockDir string, runtime *Runtime) (*Pod, error) {
return pod, nil
}
// Init() initializes all containers within a pod that have not been initialized
// Init initializes all containers within a pod that have not been initialized
func (p *Pod) Init() error {
return ErrNotImplemented
}
@ -88,7 +88,7 @@ func (p *Pod) HasContainer(id string) (bool, error) {
return p.runtime.state.PodHasContainer(p, id)
}
// AllContainersID returns the container IDs of all the containers in the pod
// AllContainersByID returns the container IDs of all the containers in the pod
func (p *Pod) AllContainersByID() ([]string, error) {
p.lock.Lock()
defer p.lock.Unlock()

View File

@ -121,8 +121,9 @@ func (r *Runtime) removeContainer(c *Container, force bool) error {
// We need to lock the pod before we lock the container
// To avoid races around removing a container and the pod it is in
var pod *Pod
var err error
if c.config.Pod != "" {
pod, err := r.state.Pod(c.config.Pod)
pod, err = r.state.Pod(c.config.Pod)
if err != nil {
return errors.Wrapf(err, "container %s is in pod %s, but pod cannot be retrieved", c.ID(), pod.ID())
}

View File

@ -754,7 +754,10 @@ func (s *SQLState) PodContainers(pod *Pod) ([]*Container, error) {
// AddPod adds a pod to the state
// Only empty pods can be added to the state
func (s *SQLState) AddPod(pod *Pod) (err error) {
const query = "INSERT INTO pods VALUES (?, ?, ?);"
const (
podQuery = "INSERT INTO pods VALUES (?, ?, ?);"
registryQuery = "INSERT INTO registry VALUES (?, ?);"
)
if !s.valid {
return ErrDBClosed
@ -781,8 +784,11 @@ func (s *SQLState) AddPod(pod *Pod) (err error) {
}
}()
_, err = tx.Exec(query, pod.ID(), pod.Name(), string(labelsJSON))
if err != nil {
if _, err := tx.Exec(registryQuery, pod.ID(), pod.Name()); err != nil {
return errors.Wrapf(err, "error adding pod %s to name/ID registry", pod.ID())
}
if _, err = tx.Exec(podQuery, pod.ID(), pod.Name(), string(labelsJSON)); err != nil {
return errors.Wrapf(err, "error adding pod %s to database", pod.ID())
}
@ -796,7 +802,10 @@ func (s *SQLState) AddPod(pod *Pod) (err error) {
// RemovePod removes a pod from the state
// Only empty pods can be removed
func (s *SQLState) RemovePod(pod *Pod) error {
const query = "DELETE FROM pods WHERE ID=?;"
const (
removePod = "DELETE FROM pods WHERE ID=?;"
removeRegistry = "DELETE FROM registry WHERE Id=?;"
)
if !s.valid {
return ErrDBClosed
@ -814,8 +823,8 @@ func (s *SQLState) RemovePod(pod *Pod) error {
}
}()
// Check rows acted on for the first transaction, verify we actually removed something
result, err := tx.Exec(query, pod.ID())
// Check rows acted on for the first statement, verify we actually removed something
result, err := tx.Exec(removePod, pod.ID())
if err != nil {
return errors.Wrapf(err, "error removing pod %s from pods table", pod.ID())
}
@ -826,6 +835,11 @@ func (s *SQLState) RemovePod(pod *Pod) error {
return ErrNoSuchPod
}
// We know it exists, remove it from registry
if _, err := tx.Exec(removeRegistry, pod.ID()); err != nil {
return errors.Wrapf(err, "error removing pod %s from name/ID registry", pod.ID())
}
if err := tx.Commit(); err != nil {
return errors.Wrapf(err, "error committing transaction to remove pod %s", pod.ID())
}
@ -891,7 +905,7 @@ func (s *SQLState) RemovePodContainers(pod *Pod) error {
return errors.Wrapf(err, "error retrieving container rows")
}
// Have container IDs, now exec SQL to remove contianers in the pod
// Have container IDs, now exec SQL to remove containers in the pod
// Remove state first, as it needs the subquery on containers
// Don't bother checking if we actually removed anything, we just want to
// empty the pod

View File

@ -173,9 +173,6 @@ func checkDB(db *sql.DB, r *Runtime) (err error) {
// Performs database setup including by not limited to initializing tables in
// the database
func prepareDB(db *sql.DB) (err error) {
// TODO create pod tables
// TODO add Pod ID to CreateStaticContainer as a FOREIGN KEY referencing podStatic(Id)
// TODO add ctr shared namespaces information - A separate table, probably? So we can FOREIGN KEY the ID
// TODO schema migration might be necessary and should be handled here
// TODO maybe make a port mappings table instead of JSONing the array and storing it?
// TODO prepared statements for common queries for performance
@ -185,6 +182,14 @@ func prepareDB(db *sql.DB) (err error) {
return errors.Wrapf(err, "error enabling foreign key support in database")
}
// Create a table for holding container and pod names and IDs
const createRegistry = `
CREATE TABLE IF NOT EXISTS registry(
Id TEXT NOT NULL PRIMARY KEY,
Name TEXT NOT NULL UNIQUE
);
`
// Create a table for unchanging container data
const createCtr = `
CREATE TABLE IF NOT EXISTS containers(
@ -235,7 +240,9 @@ func prepareDB(db *sql.DB) (err error) {
CHECK (CreateNetNS IN (0, 1)),
CHECK (Stdin IN (0, 1)),
CHECK (StopSignal>=0),
FOREIGN KEY (Id) REFERENCES containerState(Id) DEFERRABLE INITIALLY DEFERRED
FOREIGN KEY (Id) REFERENCES registry(Id) DEFERRABLE INITIALLY DEFERRED,
FOREIGN KEY (Id) REFERENCES containerState(Id) DEFERRABLE INITIALLY DEFERRED,
FOREIGN KEY (Name) REFERENCES registry(Name) DEFERRABLE INITIALLY DEFERRED,
FOREIGN KEY (Pod) REFERENCES pods(Id) DEFERRABLE INITIALLY DEFERRED,
FOREIGN KEY (IPCNsCtr) REFERENCES containers(Id) DEFERRABLE INITIALLY DEFERRED,
FOREIGN KEY (MountNsCtr) REFERENCES containers(Id) DEFERRABLE INITIALLY DEFERRED,
@ -266,6 +273,7 @@ func prepareDB(db *sql.DB) (err error) {
CHECK (State>0),
CHECK (OomKilled IN (0, 1)),
FOREIGN KEY (Id) REFERENCES registry(Id) DEFERRABLE INITIALLY DEFERRED,
FOREIGN KEY (Id) REFERENCES containers(Id) DEFERRABLE INITIALLY DEFERRED
);
`
@ -275,7 +283,9 @@ func prepareDB(db *sql.DB) (err error) {
CREATE TABLE IF NOT EXISTS pods(
Id TEXT NOT NULL PRIMARY KEY,
Name TEXT NOT NULL UNIQUE,
Labels TEXT NOT NULL
Labels TEXT NOT NULL,
FOREIGN KEY (Id) REFERENCES registry(Id) DEFERRABLE INITIALLY DEFERRED,
FOREIGN KEY (Name) REFERENCES registry(Name) DEFERRABLE INITIALLY DEFERRED
);
`
@ -293,6 +303,9 @@ func prepareDB(db *sql.DB) (err error) {
}()
if _, err := tx.Exec(createRegistry); err != nil {
return errors.Wrapf(err, "error creating ID and Name registry in database")
}
if _, err := tx.Exec(createCtr); err != nil {
return errors.Wrapf(err, "error creating containers table in database")
}
@ -720,6 +733,7 @@ func (s *SQLState) addContainer(ctr *Container) (err error) {
?, ?, ?, ?, ?,
?, ?, ?
);`
addRegistry = "INSERT INTO registry VALUES (?, ?);"
)
if !s.valid {
@ -786,6 +800,11 @@ func (s *SQLState) addContainer(ctr *Container) (err error) {
}
}()
// Add container to registry
if _, err := tx.Exec(addRegistry, ctr.ID(), ctr.Name()); err != nil {
return errors.Wrapf(err, "error adding container %s to name/ID registry", ctr.ID())
}
// Add static container information
_, err = tx.Exec(addCtr,
ctr.ID(),
@ -890,6 +909,7 @@ func (s *SQLState) removeContainer(ctr *Container) error {
const (
removeCtr = "DELETE FROM containers WHERE Id=?;"
removeState = "DELETE FROM containerState WHERE Id=?;"
removeRegistry = "DELETE FROM registry WHERE Id=?;"
)
if !s.valid {
@ -926,6 +946,12 @@ func (s *SQLState) removeContainer(ctr *Container) error {
return errors.Wrapf(err, "error removing container %s from state table", ctr.ID())
}
// Remove registry last of all
// So we know the container did exist
if _, err := tx.Exec(removeRegistry, ctr.ID()); err != nil {
return errors.Wrapf(err, "error removing container %s from name/ID registry", ctr.ID())
}
if err := tx.Commit(); err != nil {
return errors.Wrapf(err, "error committing transaction to remove container %s", ctr.ID())
}

View File

@ -4,6 +4,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
"time"
@ -79,33 +80,51 @@ func getEmptySQLState() (s State, p string, p2 string, err error) {
return state, tmpDir, lockDir, nil
}
func runForAllStates(t *testing.T, testName string, testFunc func(*testing.T, State, string)) {
func runForAllStates(t *testing.T, testFunc func(*testing.T, State, string)) {
for stateName, stateFunc := range testedStates {
state, path, lockPath, err := stateFunc()
if err != nil {
t.Fatalf("Error initializing state %s", stateName)
t.Fatalf("Error initializing state %s: %v", stateName, err)
}
defer os.RemoveAll(path)
defer state.Close()
testName = testName + "-" + stateName
success := t.Run(testName, func(t *testing.T) {
success := t.Run(stateName, func(t *testing.T) {
testFunc(t, state, lockPath)
})
if !success {
t.Fail()
t.Logf("%s failed for state %s", testName, stateName)
}
}
}
func getTestCtrN(n, lockPath string) (*Container, error) {
return getTestContainer(strings.Repeat(n, 32), "test"+n, lockPath)
}
func getTestCtr1(lockPath string) (*Container, error) {
return getTestCtrN("1", lockPath)
}
func getTestCtr2(lockPath string) (*Container, error) {
return getTestCtrN("2", lockPath)
}
func getTestPodN(n, lockPath string) (*Pod, error) {
return getTestPod(strings.Repeat(n, 32), "test"+n, lockPath)
}
func getTestPod1(lockPath string) (*Pod, error) {
return getTestPodN("1", lockPath)
}
func getTestPod2(lockPath string) (*Pod, error) {
return getTestPodN("2", lockPath)
}
func TestAddAndGetContainer(t *testing.T) {
runForAllStates(t, "TestAddAndGetContainer", addAndGetContainer)
}
func addAndGetContainer(t *testing.T, state State, lockPath string) {
testCtr, err := getTestContainer("0123456789ABCDEF0123456789ABCDEF", "test", lockPath)
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testCtr, err := getTestCtr1(lockPath)
assert.NoError(t, err)
err = state.AddContainer(testCtr)
@ -119,16 +138,14 @@ func addAndGetContainer(t *testing.T, state State, lockPath string) {
if !testContainersEqual(testCtr, retrievedCtr) {
assert.EqualValues(t, testCtr, retrievedCtr)
}
})
}
func TestAddAndGetContainerFromMultiple(t *testing.T) {
runForAllStates(t, "TestAddAndGetContainerFromMultiple", addAndGetContainerFromMultiple)
}
func addAndGetContainerFromMultiple(t *testing.T, state State, lockPath string) {
testCtr1, err := getTestContainer("11111111111111111111111111111111", "test1", lockPath)
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testCtr1, err := getTestCtr1(lockPath)
assert.NoError(t, err)
testCtr2, err := getTestContainer("22222222222222222222222222222222", "test2", lockPath)
testCtr2, err := getTestCtr2(lockPath)
assert.NoError(t, err)
err = state.AddContainer(testCtr1)
@ -145,23 +162,32 @@ func addAndGetContainerFromMultiple(t *testing.T, state State, lockPath string)
if !testContainersEqual(testCtr1, retrievedCtr) {
assert.EqualValues(t, testCtr1, retrievedCtr)
}
})
}
func TestGetContainerPodSameIDFails(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testPod, err := getTestPod1(lockPath)
assert.NoError(t, err)
err = state.AddPod(testPod)
assert.NoError(t, err)
_, err = state.Container(testPod.ID())
assert.Error(t, err)
})
}
func TestAddInvalidContainerFails(t *testing.T) {
runForAllStates(t, "TestAddInvalidContainerFails", addInvalidContainerFails)
}
func addInvalidContainerFails(t *testing.T, state State, lockPath string) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
err := state.AddContainer(&Container{config: &ContainerConfig{ID: "1234"}})
assert.Error(t, err)
})
}
func TestAddDuplicateCtrIDFails(t *testing.T) {
runForAllStates(t, "TestAddDuplicateCtrIDFails", addDuplicateCtrIDFails)
}
func addDuplicateCtrIDFails(t *testing.T, state State, lockPath string) {
testCtr1, err := getTestContainer("11111111111111111111111111111111", "test1", lockPath)
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testCtr1, err := getTestCtr1(lockPath)
assert.NoError(t, err)
testCtr2, err := getTestContainer(testCtr1.ID(), "test2", lockPath)
assert.NoError(t, err)
@ -171,16 +197,14 @@ func addDuplicateCtrIDFails(t *testing.T, state State, lockPath string) {
err = state.AddContainer(testCtr2)
assert.Error(t, err)
})
}
func TestAddDuplicateCtrNameFails(t *testing.T) {
runForAllStates(t, "TestAddDuplicateCtrNameFails", addDuplicateCtrNameFails)
}
func addDuplicateCtrNameFails(t *testing.T, state State, lockPath string) {
testCtr1, err := getTestContainer("11111111111111111111111111111111", "test1", lockPath)
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testCtr1, err := getTestCtr1(lockPath)
assert.NoError(t, err)
testCtr2, err := getTestContainer("22222222222222222222222222222222", testCtr1.Name(), lockPath)
testCtr2, err := getTestContainer(strings.Repeat("2", 32), testCtr1.Name(), lockPath)
assert.NoError(t, err)
err = state.AddContainer(testCtr1)
@ -188,50 +212,70 @@ func addDuplicateCtrNameFails(t *testing.T, state State, lockPath string) {
err = state.AddContainer(testCtr2)
assert.Error(t, err)
})
}
func TestAddCtrPodDupIDFails(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testPod, err := getTestPod1(lockPath)
assert.NoError(t, err)
testCtr, err := getTestContainer(testPod.ID(), "testCtr", lockPath)
assert.NoError(t, err)
err = state.AddPod(testPod)
assert.NoError(t, err)
err = state.AddContainer(testCtr)
assert.Error(t, err)
})
}
func TestAddCtrPodDupNameFails(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testPod, err := getTestPod1(lockPath)
assert.NoError(t, err)
testCtr, err := getTestContainer(strings.Repeat("2", 32), testPod.Name(), lockPath)
assert.NoError(t, err)
err = state.AddPod(testPod)
assert.NoError(t, err)
err = state.AddContainer(testCtr)
assert.Error(t, err)
})
}
func TestGetNonexistentContainerFails(t *testing.T) {
runForAllStates(t, "TestGetNonexistentContainerFails", getNonexistentContainerFails)
}
func getNonexistentContainerFails(t *testing.T, state State, lockPath string) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
_, err := state.Container("does not exist")
assert.Error(t, err)
})
}
func TestGetContainerWithEmptyIDFails(t *testing.T) {
runForAllStates(t, "TestGetContainerWithEmptyIDFails", getContainerWithEmptyIDFails)
}
func getContainerWithEmptyIDFails(t *testing.T, state State, lockPath string) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
_, err := state.Container("")
assert.Error(t, err)
})
}
func TestLookupContainerWithEmptyIDFails(t *testing.T) {
runForAllStates(t, "TestLookupContainerWithEmptyIDFails", lookupContainerWithEmptyIDFails)
}
func lookupContainerWithEmptyIDFails(t *testing.T, state State, lockPath string) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
_, err := state.LookupContainer("")
assert.Error(t, err)
})
}
func TestLookupNonexistentContainerFails(t *testing.T) {
runForAllStates(t, "TestLookupNonexistantContainerFails", lookupNonexistentContainerFails)
}
func lookupNonexistentContainerFails(t *testing.T, state State, lockPath string) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
_, err := state.LookupContainer("does not exist")
assert.Error(t, err)
})
}
func TestLookupContainerByFullID(t *testing.T) {
runForAllStates(t, "TestLookupContainerByFullID", lookupContainerByFullID)
}
func lookupContainerByFullID(t *testing.T, state State, lockPath string) {
testCtr, err := getTestContainer("0123456789ABCDEF0123456789ABCDEF", "test", lockPath)
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testCtr, err := getTestCtr1(lockPath)
assert.NoError(t, err)
err = state.AddContainer(testCtr)
@ -245,14 +289,12 @@ func lookupContainerByFullID(t *testing.T, state State, lockPath string) {
if !testContainersEqual(testCtr, retrievedCtr) {
assert.EqualValues(t, testCtr, retrievedCtr)
}
})
}
func TestLookupContainerByUniquePartialID(t *testing.T) {
runForAllStates(t, "TestLookupContainerByUniquePartialID", lookupContainerByUniquePartialID)
}
func lookupContainerByUniquePartialID(t *testing.T, state State, lockPath string) {
testCtr, err := getTestContainer("0123456789ABCDEF0123456789ABCDEF", "test", lockPath)
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testCtr, err := getTestCtr1(lockPath)
assert.NoError(t, err)
err = state.AddContainer(testCtr)
@ -266,16 +308,14 @@ func lookupContainerByUniquePartialID(t *testing.T, state State, lockPath string
if !testContainersEqual(testCtr, retrievedCtr) {
assert.EqualValues(t, testCtr, retrievedCtr)
}
})
}
func TestLookupContainerByNonUniquePartialIDFails(t *testing.T) {
runForAllStates(t, "TestLookupContainerByNonUniquePartialIDFails", lookupContainerByNonUniquePartialIDFails)
}
func lookupContainerByNonUniquePartialIDFails(t *testing.T, state State, lockPath string) {
testCtr1, err := getTestContainer("00000000000000000000000000000000", "test1", lockPath)
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testCtr1, err := getTestContainer(strings.Repeat("0", 32), "test1", lockPath)
assert.NoError(t, err)
testCtr2, err := getTestContainer("00000000000000000000000000000001", "test2", lockPath)
testCtr2, err := getTestContainer(strings.Repeat("0", 31)+"1", "test2", lockPath)
assert.NoError(t, err)
err = state.AddContainer(testCtr1)
@ -286,19 +326,12 @@ func lookupContainerByNonUniquePartialIDFails(t *testing.T, state State, lockPat
_, err = state.LookupContainer(testCtr1.ID()[0:8])
assert.Error(t, err)
})
}
func TestLookupContainerByName(t *testing.T) {
runForAllStates(t, "TestLookupContainerByName", lookupContainerByName)
}
func lookupContainerByName(t *testing.T, state State, lockPath string) {
state, path, lockPath, err := getEmptySQLState()
assert.NoError(t, err)
defer os.RemoveAll(path)
defer state.Close()
testCtr, err := getTestContainer("0123456789ABCDEF0123456789ABCDEF", "test", lockPath)
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testCtr, err := getTestCtr1(lockPath)
assert.NoError(t, err)
err = state.AddContainer(testCtr)
@ -312,33 +345,53 @@ func lookupContainerByName(t *testing.T, state State, lockPath string) {
if !testContainersEqual(testCtr, retrievedCtr) {
assert.EqualValues(t, testCtr, retrievedCtr)
}
})
}
func TestLookupCtrByPodNameFails(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testPod, err := getTestPod1(lockPath)
assert.NoError(t, err)
err = state.AddPod(testPod)
assert.NoError(t, err)
_, err = state.LookupContainer(testPod.Name())
assert.Error(t, err)
})
}
func TestLookupCtrByPodIDFails(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testPod, err := getTestPod1(lockPath)
assert.NoError(t, err)
err = state.AddPod(testPod)
assert.NoError(t, err)
_, err = state.LookupContainer(testPod.ID())
assert.Error(t, err)
})
}
func TestHasContainerEmptyIDFails(t *testing.T) {
runForAllStates(t, "TestHasContainerEmptyIDFails", hasContainerEmptyIDFails)
}
func hasContainerEmptyIDFails(t *testing.T, state State, lockPath string) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
_, err := state.HasContainer("")
assert.Error(t, err)
})
}
func TestHasContainerNoSuchContainerReturnsFalse(t *testing.T) {
runForAllStates(t, "TestHasContainerNoSuchContainerReturnsFalse", hasContainerNoSuchContainerReturnsFalse)
}
func hasContainerNoSuchContainerReturnsFalse(t *testing.T, state State, lockPath string) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
exists, err := state.HasContainer("does not exist")
assert.NoError(t, err)
assert.False(t, exists)
})
}
func TestHasContainerFindsContainer(t *testing.T) {
runForAllStates(t, "TestHasContainerFindsContainer", hasContainerFindsContainer)
}
func hasContainerFindsContainer(t *testing.T, state State, lockPath string) {
testCtr, err := getTestContainer("0123456789ABCDEF0123456789ABCDEF", "test", lockPath)
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testCtr, err := getTestCtr1(lockPath)
assert.NoError(t, err)
err = state.AddContainer(testCtr)
@ -347,14 +400,26 @@ func hasContainerFindsContainer(t *testing.T, state State, lockPath string) {
exists, err := state.HasContainer(testCtr.ID())
assert.NoError(t, err)
assert.True(t, exists)
})
}
func TestHasContainerPodIDIsFalse(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testPod, err := getTestPod1(lockPath)
assert.NoError(t, err)
err = state.AddPod(testPod)
assert.NoError(t, err)
exists, err := state.HasContainer(testPod.ID())
assert.NoError(t, err)
assert.False(t, exists)
})
}
func TestSaveAndUpdateContainer(t *testing.T) {
runForAllStates(t, "TestSaveAndUpdateContainer", saveAndUpdateContainer)
}
func saveAndUpdateContainer(t *testing.T, state State, lockPath string) {
testCtr, err := getTestContainer("0123456789ABCDEF0123456789ABCDEF", "test", lockPath)
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testCtr, err := getTestCtr1(lockPath)
assert.NoError(t, err)
err = state.AddContainer(testCtr)
@ -378,58 +443,48 @@ func saveAndUpdateContainer(t *testing.T, state State, lockPath string) {
if !testContainersEqual(testCtr, retrievedCtr) {
assert.EqualValues(t, testCtr, retrievedCtr)
}
})
}
func TestUpdateContainerNotInDatabaseReturnsError(t *testing.T) {
runForAllStates(t, "TestUpdateContainerNotInDatabaseReturnsError", updateContainerNotInDatabaseReturnsError)
}
func updateContainerNotInDatabaseReturnsError(t *testing.T, state State, lockPath string) {
testCtr, err := getTestContainer("0123456789ABCDEF0123456789ABCDEF", "test", lockPath)
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testCtr, err := getTestCtr1(lockPath)
assert.NoError(t, err)
err = state.UpdateContainer(testCtr)
assert.Error(t, err)
assert.False(t, testCtr.valid)
})
}
func TestUpdateInvalidContainerReturnsError(t *testing.T) {
runForAllStates(t, "TestUpdateInvalidContainerReturnsError", updateInvalidContainerReturnsError)
}
func updateInvalidContainerReturnsError(t *testing.T, state State, lockPath string) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
err := state.UpdateContainer(&Container{config: &ContainerConfig{ID: "1234"}})
assert.Error(t, err)
})
}
func TestSaveInvalidContainerReturnsError(t *testing.T) {
runForAllStates(t, "TestSaveInvalidContainerReturnsError", saveInvalidContainerReturnsError)
}
func saveInvalidContainerReturnsError(t *testing.T, state State, lockPath string) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
err := state.SaveContainer(&Container{config: &ContainerConfig{ID: "1234"}})
assert.Error(t, err)
})
}
func TestSaveContainerNotInStateReturnsError(t *testing.T) {
runForAllStates(t, "TestSaveContainerNotInStateReturnsError", saveContainerNotInStateReturnsError)
}
func saveContainerNotInStateReturnsError(t *testing.T, state State, lockPath string) {
testCtr, err := getTestContainer("0123456789ABCDEF0123456789ABCDEF", "test", lockPath)
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testCtr, err := getTestCtr1(lockPath)
assert.NoError(t, err)
err = state.SaveContainer(testCtr)
assert.Error(t, err)
assert.False(t, testCtr.valid)
})
}
func TestRemoveContainer(t *testing.T) {
runForAllStates(t, "TestRemoveContainer", removeContainer)
}
func removeContainer(t *testing.T, state State, lockPath string) {
testCtr, err := getTestContainer("0123456789ABCDEF0123456789ABCDEF", "test", lockPath)
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testCtr, err := getTestCtr1(lockPath)
assert.NoError(t, err)
err = state.AddContainer(testCtr)
@ -445,36 +500,30 @@ func removeContainer(t *testing.T, state State, lockPath string) {
ctrs2, err := state.AllContainers()
assert.NoError(t, err)
assert.Equal(t, 0, len(ctrs2))
})
}
func TestRemoveNonexistantContainerFails(t *testing.T) {
runForAllStates(t, "TestRemoveNonexistantContainerFails", removeNonexistantContainerFails)
}
func removeNonexistantContainerFails(t *testing.T, state State, lockPath string) {
testCtr, err := getTestContainer("0123456789ABCDEF0123456789ABCDEF", "test", lockPath)
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testCtr, err := getTestCtr1(lockPath)
assert.NoError(t, err)
err = state.RemoveContainer(testCtr)
assert.Error(t, err)
})
}
func TestGetAllContainersOnNewStateIsEmpty(t *testing.T) {
runForAllStates(t, "TestGetAllContainersOnNewStateIsEmpty", getAllContainersOnNewStateIsEmpty)
}
func getAllContainersOnNewStateIsEmpty(t *testing.T, state State, lockPath string) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
ctrs, err := state.AllContainers()
assert.NoError(t, err)
assert.Equal(t, 0, len(ctrs))
})
}
func TestGetAllContainersWithOneContainer(t *testing.T) {
runForAllStates(t, "TestGetAllContainersWithOneContainer", getAllContainersWithOneContainer)
}
func getAllContainersWithOneContainer(t *testing.T, state State, lockPath string) {
testCtr, err := getTestContainer("0123456789ABCDEF0123456789ABCDEF", "test", lockPath)
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testCtr, err := getTestCtr1(lockPath)
assert.NoError(t, err)
err = state.AddContainer(testCtr)
@ -489,16 +538,14 @@ func getAllContainersWithOneContainer(t *testing.T, state State, lockPath string
if !testContainersEqual(testCtr, ctrs[0]) {
assert.EqualValues(t, testCtr, ctrs[0])
}
})
}
func TestGetAllContainersTwoContainers(t *testing.T) {
runForAllStates(t, "TestGetAllContainersTwoContainers", getAllContainersTwoContainers)
}
func getAllContainersTwoContainers(t *testing.T, state State, lockPath string) {
testCtr1, err := getTestContainer("11111111111111111111111111111111", "test1", lockPath)
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testCtr1, err := getTestCtr1(lockPath)
assert.NoError(t, err)
testCtr2, err := getTestContainer("22222222222222222222222222222222", "test2", lockPath)
testCtr2, err := getTestCtr2(lockPath)
assert.NoError(t, err)
err = state.AddContainer(testCtr1)
@ -510,25 +557,21 @@ func getAllContainersTwoContainers(t *testing.T, state State, lockPath string) {
ctrs, err := state.AllContainers()
assert.NoError(t, err)
assert.Equal(t, 2, len(ctrs))
})
}
func TestContainerInUseInvalidContainer(t *testing.T) {
runForAllStates(t, "TestContainerInUseInvalidContainer", containerInUseInvalidContainer)
}
func containerInUseInvalidContainer(t *testing.T, state State, lockPath string) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
_, err := state.ContainerInUse(&Container{})
assert.Error(t, err)
})
}
func TestContainerInUseOneContainer(t *testing.T) {
runForAllStates(t, "TestContainerInUseOneContainer", containerInUseOneContainer)
}
func containerInUseOneContainer(t *testing.T, state State, lockPath string) {
testCtr1, err := getTestContainer("11111111111111111111111111111111", "test1", lockPath)
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testCtr1, err := getTestCtr1(lockPath)
assert.NoError(t, err)
testCtr2, err := getTestContainer("22222222222222222222222222222222", "test2", lockPath)
testCtr2, err := getTestCtr2(lockPath)
assert.NoError(t, err)
testCtr2.config.UserNsCtr = testCtr1.config.ID
@ -543,18 +586,16 @@ func containerInUseOneContainer(t *testing.T, state State, lockPath string) {
assert.NoError(t, err)
assert.Equal(t, 1, len(ids))
assert.Equal(t, testCtr2.config.ID, ids[0])
})
}
func TestContainerInUseTwoContainers(t *testing.T) {
runForAllStates(t, "TestContainerInUseTwoContainers", containerInUseTwoContainers)
}
func containerInUseTwoContainers(t *testing.T, state State, lockPath string) {
testCtr1, err := getTestContainer("11111111111111111111111111111111", "test1", lockPath)
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testCtr1, err := getTestCtr1(lockPath)
assert.NoError(t, err)
testCtr2, err := getTestContainer("22222222222222222222222222222222", "test2", lockPath)
testCtr2, err := getTestCtr2(lockPath)
assert.NoError(t, err)
testCtr3, err := getTestContainer("33333333333333333333333333333333", "test3", lockPath)
testCtr3, err := getTestCtrN("3", lockPath)
assert.NoError(t, err)
testCtr2.config.UserNsCtr = testCtr1.config.ID
@ -572,16 +613,37 @@ func containerInUseTwoContainers(t *testing.T, state State, lockPath string) {
ids, err := state.ContainerInUse(testCtr1)
assert.NoError(t, err)
assert.Equal(t, 2, len(ids))
})
}
func TestContainerInUseOneContainerMultipleDependencies(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testCtr1, err := getTestCtr1(lockPath)
assert.NoError(t, err)
testCtr2, err := getTestCtr2(lockPath)
assert.NoError(t, err)
testCtr2.config.UserNsCtr = testCtr1.config.ID
testCtr2.config.IPCNsCtr = testCtr1.config.ID
err = state.AddContainer(testCtr1)
assert.NoError(t, err)
err = state.AddContainer(testCtr2)
assert.NoError(t, err)
ids, err := state.ContainerInUse(testCtr1)
assert.NoError(t, err)
assert.Equal(t, 1, len(ids))
assert.Equal(t, testCtr2.config.ID, ids[0])
})
}
func TestCannotRemoveContainerWithDependency(t *testing.T) {
runForAllStates(t, "TestCannotRemoveContainerWithDependency", cannotRemoveContainerWithDependency)
}
func cannotRemoveContainerWithDependency(t *testing.T, state State, lockPath string) {
testCtr1, err := getTestContainer("11111111111111111111111111111111", "test1", lockPath)
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testCtr1, err := getTestCtr1(lockPath)
assert.NoError(t, err)
testCtr2, err := getTestContainer("22222222222222222222222222222222", "test2", lockPath)
testCtr2, err := getTestCtr2(lockPath)
assert.NoError(t, err)
testCtr2.config.UserNsCtr = testCtr1.config.ID
@ -594,19 +656,17 @@ func cannotRemoveContainerWithDependency(t *testing.T, state State, lockPath str
err = state.RemoveContainer(testCtr1)
assert.Error(t, err)
})
}
func TestCanRemoveContainerAfterDependencyRemoved(t *testing.T) {
runForAllStates(t, "TestCanRemoveContainerAfterDependencyRemoved", canRemoveContainerAfterDependencyRemoved)
}
func canRemoveContainerAfterDependencyRemoved(t *testing.T, state State, lockPath string) {
testCtr1, err := getTestContainer("11111111111111111111111111111111", "test1", lockPath)
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testCtr1, err := getTestCtr1(lockPath)
assert.NoError(t, err)
testCtr2, err := getTestContainer("22222222222222222222222222222222", "test2", lockPath)
testCtr2, err := getTestCtr2(lockPath)
assert.NoError(t, err)
testCtr2.config.UserNsCtr = testCtr1.config.ID
testCtr2.config.UserNsCtr = testCtr1.ID()
err = state.AddContainer(testCtr1)
assert.NoError(t, err)
@ -619,4 +679,235 @@ func canRemoveContainerAfterDependencyRemoved(t *testing.T, state State, lockPat
err = state.RemoveContainer(testCtr1)
assert.NoError(t, err)
})
}
func TestCanRemoveContainerAfterDependencyRemovedDuplicate(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testCtr1, err := getTestCtr1(lockPath)
assert.NoError(t, err)
testCtr2, err := getTestCtr2(lockPath)
assert.NoError(t, err)
testCtr2.config.UserNsCtr = testCtr1.ID()
testCtr2.config.IPCNsCtr = testCtr1.ID()
err = state.AddContainer(testCtr1)
assert.NoError(t, err)
err = state.AddContainer(testCtr2)
assert.NoError(t, err)
err = state.RemoveContainer(testCtr2)
assert.NoError(t, err)
err = state.RemoveContainer(testCtr1)
assert.NoError(t, err)
})
}
func TestGetPodDoesNotExist(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
_, err := state.Pod("doesnotexist")
assert.Error(t, err)
})
}
func TestGetPodEmptyID(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
_, err := state.Pod("")
assert.Error(t, err)
})
}
func TestGetPodOnePod(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testPod, err := getTestPod1(lockPath)
assert.NoError(t, err)
err = state.AddPod(testPod)
assert.NoError(t, err)
statePod, err := state.Pod(testPod.ID())
assert.NoError(t, err)
// Use assert.EqualValues if the test fails to pretty print diff
// between actual and expected
if !testPodsEqual(testPod, statePod) {
assert.EqualValues(t, testPod, statePod)
}
})
}
func TestGetOnePodFromTwo(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testPod1, err := getTestPod1(lockPath)
assert.NoError(t, err)
testPod2, err := getTestPod2(lockPath)
assert.NoError(t, err)
err = state.AddPod(testPod1)
assert.NoError(t, err)
err = state.AddPod(testPod2)
assert.NoError(t, err)
statePod, err := state.Pod(testPod1.ID())
assert.NoError(t, err)
// Use assert.EqualValues if the test fails to pretty print diff
// between actual and expected
if !testPodsEqual(testPod1, statePod) {
assert.EqualValues(t, testPod1, statePod)
}
})
}
func TestGetNotExistPodWithPods(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testPod1, err := getTestPod1(lockPath)
assert.NoError(t, err)
testPod2, err := getTestPod2(lockPath)
assert.NoError(t, err)
err = state.AddPod(testPod1)
assert.NoError(t, err)
err = state.AddPod(testPod2)
assert.NoError(t, err)
_, err = state.Pod("notexist")
assert.Error(t, err)
})
}
func TestGetPodByCtrID(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testCtr, err := getTestCtr1(lockPath)
assert.NoError(t, err)
err = state.AddContainer(testCtr)
assert.NoError(t, err)
_, err = state.Pod(testCtr.ID())
assert.Error(t, err)
})
}
func TestLookupPodEmptyID(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
_, err := state.LookupPod("")
assert.Error(t, err)
})
}
func TestLookupNotExistPod(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
_, err := state.LookupPod("doesnotexist")
assert.Error(t, err)
})
}
func TestLookupPodFullID(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testPod, err := getTestPod1(lockPath)
assert.NoError(t, err)
err = state.AddPod(testPod)
assert.NoError(t, err)
statePod, err := state.LookupPod(testPod.ID())
assert.NoError(t, err)
// Use assert.EqualValues if the test fails to pretty print diff
// between actual and expected
if !testPodsEqual(testPod, statePod) {
assert.EqualValues(t, testPod, statePod)
}
})
}
func TestLookupPodUniquePartialID(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testPod, err := getTestPod1(lockPath)
assert.NoError(t, err)
err = state.AddPod(testPod)
assert.NoError(t, err)
statePod, err := state.LookupPod(testPod.ID()[0:8])
assert.NoError(t, err)
// Use assert.EqualValues if the test fails to pretty print diff
// between actual and expected
if !testPodsEqual(testPod, statePod) {
assert.EqualValues(t, testPod, statePod)
}
})
}
func TestLookupPodNonUniquePartialID(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testPod1, err := getTestPod(strings.Repeat("1", 32), "test1", lockPath)
assert.NoError(t, err)
testPod2, err := getTestPod(strings.Repeat("1", 31)+"2", "test2", lockPath)
assert.NoError(t, err)
err = state.AddPod(testPod1)
assert.NoError(t, err)
err = state.AddPod(testPod2)
assert.NoError(t, err)
_, err = state.LookupPod(testPod1.ID()[0:8])
assert.Error(t, err)
})
}
func TestLookupPodByName(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testPod, err := getTestPod1(lockPath)
assert.NoError(t, err)
err = state.AddPod(testPod)
assert.NoError(t, err)
statePod, err := state.LookupPod(testPod.Name())
assert.NoError(t, err)
// Use assert.EqualValues if the test fails to pretty print diff
// between actual and expected
if !testPodsEqual(testPod, statePod) {
assert.EqualValues(t, testPod, statePod)
}
})
}
func TestLookupPodByCtrID(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testCtr, err := getTestCtr1(lockPath)
assert.NoError(t, err)
err = state.AddContainer(testCtr)
assert.NoError(t, err)
_, err = state.LookupPod(testCtr.ID())
assert.Error(t, err)
})
}
func TestLookupPodByCtrName(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, lockPath string) {
testCtr, err := getTestCtr1(lockPath)
assert.NoError(t, err)
err = state.AddContainer(testCtr)
assert.NoError(t, err)
_, err = state.LookupPod(testCtr.Name())
assert.Error(t, err)
})
}

View File

@ -24,7 +24,7 @@ func getTestContainer(id, name, locksDir string) (*Container, error) {
StaticDir: "/does/not/exist/",
LogPath: "/does/not/exist/",
Stdin: true,
Labels: make(map[string]string),
Labels: map[string]string{"a": "b", "c": "d"},
StopSignal: 0,
StopTimeout: 0,
CreatedTime: time.Now(),
@ -74,6 +74,25 @@ func getTestContainer(id, name, locksDir string) (*Container, error) {
return ctr, nil
}
// nolint
func getTestPod(id, name, locksDir string) (*Pod, error) {
pod := &Pod{
id: id,
name: name,
labels: map[string]string{"a": "b", "c": "d"},
valid: true,
}
lockPath := filepath.Join(locksDir, id)
lock, err := storage.GetLockfile(lockPath)
if err != nil {
return nil, err
}
pod.lock = lock
return pod, nil
}
// This horrible hack tests if containers are equal in a way that should handle
// empty arrays being dropped to nil pointers in the spec JSON
// nolint
@ -114,3 +133,26 @@ func testContainersEqual(a, b *Container) bool {
return reflect.DeepEqual(aStateJSON, bStateJSON)
}
// This tests pod equality
// We cannot guarantee equality in lockfile objects so we can't simply compare
// nolint
func testPodsEqual(a, b *Pod) bool {
if a == nil && b == nil {
return true
} else if a == nil || b == nil {
return false
}
if a.id != b.id {
return false
}
if a.name != b.name {
return false
}
if a.valid != b.valid {
return false
}
return reflect.DeepEqual(a.labels, b.labels)
}