summaryrefslogtreecommitdiff
path: root/libpod/state_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'libpod/state_test.go')
-rw-r--r--libpod/state_test.go627
1 files changed, 627 insertions, 0 deletions
diff --git a/libpod/state_test.go b/libpod/state_test.go
new file mode 100644
index 000000000..ac84a63d9
--- /dev/null
+++ b/libpod/state_test.go
@@ -0,0 +1,627 @@
+package libpod
+
+import (
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "testing"
+ "time"
+
+ "github.com/containers/storage"
+ "github.com/stretchr/testify/assert"
+)
+
+// Returns state, tmp directory containing all state files, locks directory
+// (subdirectory of tmp dir), and error
+// Closing the state and removing the given tmp directory should be sufficient
+// to clean up
+type emptyStateFunc func() (State, string, string, error)
+
+const (
+ tmpDirPrefix = "libpod_state_test_"
+)
+
+var (
+ testedStates = map[string]emptyStateFunc{
+ "sql": getEmptySQLState,
+ "in-memory": getEmptyInMemoryState,
+ }
+)
+
+// Get an empty in-memory state for use in tests
+func getEmptyInMemoryState() (s State, p string, p2 string, err error) {
+ tmpDir, err := ioutil.TempDir("", tmpDirPrefix)
+ if err != nil {
+ return nil, "", "", err
+ }
+ defer func() {
+ if err != nil {
+ os.RemoveAll(tmpDir)
+ }
+ }()
+
+ state, err := NewInMemoryState()
+ if err != nil {
+ return nil, "", "", err
+ }
+
+ // Don't need a separate locks dir as InMemoryState stores nothing on
+ // disk
+ return state, tmpDir, tmpDir, nil
+}
+
+// Get an empty SQL state for use in tests
+// An empty Runtime is provided
+func getEmptySQLState() (s State, p string, p2 string, err error) {
+ tmpDir, err := ioutil.TempDir("", tmpDirPrefix)
+ if err != nil {
+ return nil, "", "", err
+ }
+ defer func() {
+ if err != nil {
+ os.RemoveAll(tmpDir)
+ }
+ }()
+
+ dbPath := filepath.Join(tmpDir, "db.sql")
+ specsDir := filepath.Join(tmpDir, "specs")
+ lockDir := filepath.Join(tmpDir, "locks")
+
+ runtime := new(Runtime)
+ runtime.config = new(RuntimeConfig)
+ runtime.config.StorageConfig = storage.StoreOptions{}
+
+ state, err := NewSQLState(dbPath, specsDir, lockDir, runtime)
+ if err != nil {
+ return nil, "", "", err
+ }
+
+ return state, tmpDir, lockDir, nil
+}
+
+func runForAllStates(t *testing.T, testName string, 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)
+ }
+ defer os.RemoveAll(path)
+ defer state.Close()
+
+ testName = testName + "-" + stateName
+
+ success := t.Run(testName, func(t *testing.T) {
+ testFunc(t, state, lockPath)
+ })
+ if !success {
+ t.Fail()
+ t.Logf("%s failed for state %s", testName, stateName)
+ }
+ }
+}
+
+func TestAddAndGetContainer(t *testing.T) {
+ runForAllStates(t, "TestAddAndGetContainer", addAndGetContainer)
+}
+
+func addAndGetContainer(t *testing.T, state State, lockPath string) {
+ testCtr, err := getTestContainer("0123456789ABCDEF0123456789ABCDEF", "test", lockPath)
+ assert.NoError(t, err)
+
+ err = state.AddContainer(testCtr)
+ assert.NoError(t, err)
+
+ retrievedCtr, err := state.Container(testCtr.ID())
+ assert.NoError(t, err)
+
+ // Use assert.EqualValues if the test fails to pretty print diff
+ // between actual and expected
+ 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)
+ assert.NoError(t, err)
+ testCtr2, err := getTestContainer("22222222222222222222222222222222", "test2", lockPath)
+ assert.NoError(t, err)
+
+ err = state.AddContainer(testCtr1)
+ assert.NoError(t, err)
+
+ err = state.AddContainer(testCtr2)
+ assert.NoError(t, err)
+
+ retrievedCtr, err := state.Container(testCtr1.ID())
+ assert.NoError(t, err)
+
+ // Use assert.EqualValues if the test fails to pretty print diff
+ // between actual and expected
+ if !testContainersEqual(testCtr1, retrievedCtr) {
+ assert.EqualValues(t, testCtr1, retrievedCtr)
+ }
+}
+
+func TestAddInvalidContainerFails(t *testing.T) {
+ runForAllStates(t, "TestAddInvalidContainerFails", addInvalidContainerFails)
+}
+
+func addInvalidContainerFails(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)
+ assert.NoError(t, err)
+ testCtr2, err := getTestContainer(testCtr1.ID(), "test2", lockPath)
+ assert.NoError(t, err)
+
+ err = state.AddContainer(testCtr1)
+ assert.NoError(t, err)
+
+ 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)
+ assert.NoError(t, err)
+ testCtr2, err := getTestContainer("22222222222222222222222222222222", testCtr1.Name(), lockPath)
+ assert.NoError(t, err)
+
+ err = state.AddContainer(testCtr1)
+ assert.NoError(t, err)
+
+ err = state.AddContainer(testCtr2)
+ assert.Error(t, err)
+}
+
+func TestGetNonexistentContainerFails(t *testing.T) {
+ runForAllStates(t, "TestGetNonexistentContainerFails", getNonexistentContainerFails)
+}
+
+func getNonexistentContainerFails(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) {
+ _, 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) {
+ _, 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) {
+ _, 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)
+ assert.NoError(t, err)
+
+ err = state.AddContainer(testCtr)
+ assert.NoError(t, err)
+
+ retrievedCtr, err := state.LookupContainer(testCtr.ID())
+ assert.NoError(t, err)
+
+ // Use assert.EqualValues if the test fails to pretty print diff
+ // between actual and expected
+ 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)
+ assert.NoError(t, err)
+
+ err = state.AddContainer(testCtr)
+ assert.NoError(t, err)
+
+ retrievedCtr, err := state.LookupContainer(testCtr.ID()[0:8])
+ assert.NoError(t, err)
+
+ // Use assert.EqualValues if the test fails to pretty print diff
+ // between actual and expected
+ 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)
+ assert.NoError(t, err)
+ testCtr2, err := getTestContainer("00000000000000000000000000000001", "test2", lockPath)
+ assert.NoError(t, err)
+
+ err = state.AddContainer(testCtr1)
+ assert.NoError(t, err)
+
+ err = state.AddContainer(testCtr2)
+ assert.NoError(t, err)
+
+ _, 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)
+ assert.NoError(t, err)
+
+ err = state.AddContainer(testCtr)
+ assert.NoError(t, err)
+
+ retrievedCtr, err := state.LookupContainer(testCtr.Name())
+ assert.NoError(t, err)
+
+ // Use assert.EqualValues if the test fails to pretty print diff
+ // between actual and expected
+ if !testContainersEqual(testCtr, retrievedCtr) {
+ assert.EqualValues(t, testCtr, retrievedCtr)
+ }
+}
+
+func TestHasContainerEmptyIDFails(t *testing.T) {
+ runForAllStates(t, "TestHasContainerEmptyIDFails", hasContainerEmptyIDFails)
+}
+
+func hasContainerEmptyIDFails(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) {
+ 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)
+ assert.NoError(t, err)
+
+ err = state.AddContainer(testCtr)
+ assert.NoError(t, err)
+
+ exists, err := state.HasContainer(testCtr.ID())
+ assert.NoError(t, err)
+ assert.True(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)
+ assert.NoError(t, err)
+
+ err = state.AddContainer(testCtr)
+ assert.NoError(t, err)
+
+ retrievedCtr, err := state.Container(testCtr.ID())
+ assert.NoError(t, err)
+
+ retrievedCtr.state.State = ContainerStateStopped
+ retrievedCtr.state.ExitCode = 127
+ retrievedCtr.state.FinishedTime = time.Now()
+
+ err = state.SaveContainer(retrievedCtr)
+ assert.NoError(t, err)
+
+ err = state.UpdateContainer(testCtr)
+ assert.NoError(t, err)
+
+ // Use assert.EqualValues if the test fails to pretty print diff
+ // between actual and expected
+ 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)
+ 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) {
+ 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) {
+ 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)
+ 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)
+ assert.NoError(t, err)
+
+ err = state.AddContainer(testCtr)
+ assert.NoError(t, err)
+
+ ctrs, err := state.AllContainers()
+ assert.NoError(t, err)
+ assert.Equal(t, 1, len(ctrs))
+
+ err = state.RemoveContainer(testCtr)
+ assert.NoError(t, err)
+
+ 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)
+ 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) {
+ 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)
+ assert.NoError(t, err)
+
+ err = state.AddContainer(testCtr)
+ assert.NoError(t, err)
+
+ ctrs, err := state.AllContainers()
+ assert.NoError(t, err)
+ assert.Equal(t, 1, len(ctrs))
+
+ // Use assert.EqualValues if the test fails to pretty print diff
+ // between actual and expected
+ 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)
+ assert.NoError(t, err)
+ testCtr2, err := getTestContainer("22222222222222222222222222222222", "test2", lockPath)
+ assert.NoError(t, err)
+
+ err = state.AddContainer(testCtr1)
+ assert.NoError(t, err)
+
+ err = state.AddContainer(testCtr2)
+ assert.NoError(t, err)
+
+ ctrs, err := state.AllContainers()
+ assert.NoError(t, err)
+ assert.Equal(t, 2, len(ctrs))
+}
+
+func TestContainerInUseInvalidContainer(t *testing.T) {
+ state, path, _, err := getEmptyState()
+ assert.NoError(t, err)
+ defer os.RemoveAll(path)
+ defer state.Close()
+
+ _, err = state.ContainerInUse(&Container{})
+ assert.Error(t, err)
+}
+
+func TestContainerInUseOneContainer(t *testing.T) {
+ state, path, lockPath, err := getEmptyState()
+ assert.NoError(t, err)
+ defer os.RemoveAll(path)
+ defer state.Close()
+
+ testCtr1, err := getTestContainer("11111111111111111111111111111111", "test1", lockPath)
+ assert.NoError(t, err)
+ testCtr2, err := getTestContainer("22222222222222222222222222222222", "test2", lockPath)
+ assert.NoError(t, err)
+
+ testCtr2.config.UserNsCtr = 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 TestContainerInUseTwoContainers(t *testing.T) {
+ state, path, lockPath, err := getEmptyState()
+ assert.NoError(t, err)
+ defer os.RemoveAll(path)
+ defer state.Close()
+
+ testCtr1, err := getTestContainer("11111111111111111111111111111111", "test1", lockPath)
+ assert.NoError(t, err)
+ testCtr2, err := getTestContainer("22222222222222222222222222222222", "test2", lockPath)
+ assert.NoError(t, err)
+ testCtr3, err := getTestContainer("33333333333333333333333333333333", "test3", lockPath)
+ assert.NoError(t, err)
+
+ testCtr2.config.UserNsCtr = testCtr1.config.ID
+ testCtr3.config.IPCNsCtr = testCtr1.config.ID
+
+ err = state.AddContainer(testCtr1)
+ assert.NoError(t, err)
+
+ err = state.AddContainer(testCtr2)
+ assert.NoError(t, err)
+
+ err = state.AddContainer(testCtr3)
+ assert.NoError(t, err)
+
+ ids, err := state.ContainerInUse(testCtr1)
+ assert.NoError(t, err)
+ assert.Equal(t, 2, len(ids))
+}
+
+func TestCannotRemoveContainerWithDependency(t *testing.T) {
+ state, path, lockPath, err := getEmptyState()
+ assert.NoError(t, err)
+ defer os.RemoveAll(path)
+ defer state.Close()
+
+ testCtr1, err := getTestContainer("11111111111111111111111111111111", "test1", lockPath)
+ assert.NoError(t, err)
+ testCtr2, err := getTestContainer("22222222222222222222222222222222", "test2", lockPath)
+ assert.NoError(t, err)
+
+ testCtr2.config.UserNsCtr = testCtr1.config.ID
+
+ err = state.AddContainer(testCtr1)
+ assert.NoError(t, err)
+
+ err = state.AddContainer(testCtr2)
+ assert.NoError(t, err)
+
+ err = state.RemoveContainer(testCtr1)
+ assert.Error(t, err)
+}
+
+func TestCanRemoveContainerAfterDependencyRemoved(t *testing.T) {
+ state, path, lockPath, err := getEmptyState()
+ assert.NoError(t, err)
+ defer os.RemoveAll(path)
+ defer state.Close()
+
+ testCtr1, err := getTestContainer("11111111111111111111111111111111", "test1", lockPath)
+ assert.NoError(t, err)
+ testCtr2, err := getTestContainer("22222222222222222222222222222222", "test2", lockPath)
+ assert.NoError(t, err)
+
+ testCtr2.config.UserNsCtr = testCtr1.config.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)
+}