Skip to content

Commit

Permalink
Merge pull request #72 from mheon/file_locking
Browse files Browse the repository at this point in the history
Move containers to file locks from c/storage
  • Loading branch information
rhatdan committed Dec 11, 2017
2 parents 62e19be + 190b052 commit 12682aa
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 101 deletions.
34 changes: 20 additions & 14 deletions libpod/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"os"
"path/filepath"
"strconv"
"sync"
"syscall"
"time"

Expand Down Expand Up @@ -64,9 +63,8 @@ type Container struct {

state *containerRuntimeInfo

// TODO move to storage.Locker from sync.Mutex
valid bool
lock sync.Mutex
lock storage.Locker
runtime *Runtime
}

Expand Down Expand Up @@ -319,6 +317,8 @@ func (c *Container) attachSocketPath() string {

// Sync this container with on-disk state and runc status
// Should only be called with container lock held
// This function should suffice to ensure a container's state is accurate and
// it is valid for use.
func (c *Container) syncContainer() error {
if err := c.runtime.state.UpdateContainer(c); err != nil {
return err
Expand All @@ -343,7 +343,7 @@ func (c *Container) syncContainer() error {
}

// Make a new container
func newContainer(rspec *spec.Spec) (*Container, error) {
func newContainer(rspec *spec.Spec, lockDir string) (*Container, error) {
if rspec == nil {
return nil, errors.Wrapf(ErrInvalidArg, "must provide a valid runtime spec to create container")
}
Expand All @@ -360,6 +360,20 @@ func newContainer(rspec *spec.Spec) (*Container, error) {

ctr.config.CreatedTime = time.Now()

// Path our lock file will reside at
lockPath := filepath.Join(lockDir, ctr.config.ID)
// Ensure there is no conflict - file does not exist
_, err := os.Stat(lockPath)
if err == nil || !os.IsNotExist(err) {
return nil, errors.Wrapf(ErrCtrExists, "lockfile for container ID %s already exists", ctr.config.ID)
}
// Grab a lockfile at the given path
lock, err := storage.GetLockfile(lockPath)
if err != nil {
return nil, errors.Wrapf(err, "error creating lockfile for new container")
}
ctr.lock = lock

return ctr, nil
}

Expand Down Expand Up @@ -904,11 +918,7 @@ func (c *Container) mountStorage() (err error) {
}
}()

if err := c.runtime.state.SaveContainer(c); err != nil {
return errors.Wrapf(err, "error saving container %s state", c.ID())
}

return nil
return c.save()
}

// CleanupStorage unmounts all mount points in container and cleans up container storage
Expand Down Expand Up @@ -944,9 +954,5 @@ func (c *Container) cleanupStorage() error {
c.state.Mountpoint = ""
c.state.Mounted = false

if err := c.runtime.state.SaveContainer(c); err != nil {
return errors.Wrapf(err, "error saving container %s state", c.ID())
}

return nil
return c.save()
}
15 changes: 13 additions & 2 deletions libpod/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type Runtime struct {
storageService *storageService
imageContext *types.SystemContext
ociRuntime *OCIRuntime
lockDir string
valid bool
lock sync.RWMutex
}
Expand Down Expand Up @@ -136,6 +137,17 @@ func NewRuntime(options ...RuntimeOption) (runtime *Runtime, err error) {
}
}

// Make a directory to hold container lockfiles
lockDir := filepath.Join(runtime.config.StaticDir, "lock")
if err := os.MkdirAll(lockDir, 0755); err != nil {
// The directory is allowed to exist
if !os.IsExist(err) {
return nil, errors.Wrapf(err, "error creating runtime lockfiles directory %s",
lockDir)
}
}
runtime.lockDir = lockDir

// Make the per-boot files directory if it does not exist
if err := os.MkdirAll(runtime.config.TmpDir, 0755); err != nil {
// The directory is allowed to exist
Expand All @@ -154,7 +166,6 @@ func NewRuntime(options ...RuntimeOption) (runtime *Runtime, err error) {
runtime.state = state
} else {
dbPath := filepath.Join(runtime.config.StaticDir, "state.sql")
lockPath := filepath.Join(runtime.config.TmpDir, "state.lck")
specsDir := filepath.Join(runtime.config.StaticDir, "ocispec")

// Make a directory to hold JSON versions of container OCI specs
Expand All @@ -166,7 +177,7 @@ func NewRuntime(options ...RuntimeOption) (runtime *Runtime, err error) {
}
}

state, err := NewSQLState(dbPath, lockPath, specsDir, runtime)
state, err := NewSQLState(dbPath, specsDir, runtime.lockDir, runtime)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion libpod/runtime_ctr.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func (r *Runtime) NewContainer(spec *spec.Spec, options ...CtrCreateOption) (c *
return nil, ErrRuntimeStopped
}

ctr, err := newContainer(spec)
ctr, err := newContainer(spec, r.lockDir)
if err != nil {
return nil, err
}
Expand Down
41 changes: 13 additions & 28 deletions libpod/sql_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"io/ioutil"
"os"

"github.com/containers/storage"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"

Expand All @@ -22,24 +21,17 @@ const DBSchema = 2
type SQLState struct {
db *sql.DB
specsDir string
lockDir string
runtime *Runtime
lock storage.Locker
valid bool
}

// NewSQLState initializes a SQL-backed state, created the database if necessary
func NewSQLState(dbPath, lockPath, specsDir string, runtime *Runtime) (State, error) {
func NewSQLState(dbPath, specsDir, lockDir string, runtime *Runtime) (State, error) {
state := new(SQLState)

state.runtime = runtime

// Make our lock file
lock, err := storage.GetLockfile(lockPath)
if err != nil {
return nil, errors.Wrapf(err, "error creating lockfile for state")
}
state.lock = lock

// Make the directory that will hold JSON copies of container runtime specs
if err := os.MkdirAll(specsDir, 0750); err != nil {
// The directory is allowed to exist
Expand All @@ -49,9 +41,14 @@ func NewSQLState(dbPath, lockPath, specsDir string, runtime *Runtime) (State, er
}
state.specsDir = specsDir

// Acquire the lock while we open the database and perform initial setup
state.lock.Lock()
defer state.lock.Unlock()
// Make the directory that will hold container lockfiles
if err := os.MkdirAll(lockDir, 0750); err != nil {
// The directory is allowed to exist
if !os.IsExist(err) {
return nil, errors.Wrapf(err, "error creating lockfiles dir %s", lockDir)
}
}
state.lockDir = lockDir

// TODO add a separate temporary database for per-boot container
// state
Expand Down Expand Up @@ -87,9 +84,6 @@ func NewSQLState(dbPath, lockPath, specsDir string, runtime *Runtime) (State, er

// Close the state's database connection
func (s *SQLState) Close() error {
s.lock.Lock()
defer s.lock.Unlock()

if !s.valid {
return ErrDBClosed
}
Expand Down Expand Up @@ -176,7 +170,7 @@ func (s *SQLState) Container(id string) (*Container, error) {

row := s.db.QueryRow(query, id)

ctr, err := ctrFromScannable(row, s.runtime, s.specsDir)
ctr, err := ctrFromScannable(row, s.runtime, s.specsDir, s.lockDir)
if err != nil {
return nil, errors.Wrapf(err, "error retrieving container %s from database", id)
}
Expand Down Expand Up @@ -223,7 +217,7 @@ func (s *SQLState) LookupContainer(idOrName string) (*Container, error) {
}

var err error
ctr, err = ctrFromScannable(rows, s.runtime, s.specsDir)
ctr, err = ctrFromScannable(rows, s.runtime, s.specsDir, s.lockDir)
if err != nil {
return nil, errors.Wrapf(err, "error retrieving container %s from database", idOrName)
}
Expand Down Expand Up @@ -296,9 +290,6 @@ func (s *SQLState) AddContainer(ctr *Container) (err error) {
return errors.Wrapf(err, "error marshaling container %s labels to JSON", ctr.ID())
}

s.lock.Lock()
defer s.lock.Unlock()

tx, err := s.db.Begin()
if err != nil {
return errors.Wrapf(err, "error beginning database transaction")
Expand Down Expand Up @@ -477,9 +468,6 @@ func (s *SQLState) SaveContainer(ctr *Container) error {
Pid=?
WHERE Id=?;`

s.lock.Lock()
defer s.lock.Unlock()

if !s.valid {
return ErrDBClosed
}
Expand Down Expand Up @@ -537,9 +525,6 @@ func (s *SQLState) RemoveContainer(ctr *Container) error {
removeState = "DELETE FROM containerState WHERE ID=?;"
)

s.lock.Lock()
defer s.lock.Unlock()

if !s.valid {
return ErrDBClosed
}
Expand Down Expand Up @@ -622,7 +607,7 @@ func (s *SQLState) AllContainers() ([]*Container, error) {
containers := []*Container{}

for rows.Next() {
ctr, err := ctrFromScannable(rows, s.runtime, s.specsDir)
ctr, err := ctrFromScannable(rows, s.runtime, s.specsDir, s.lockDir)
if err != nil {
return nil, err
}
Expand Down
11 changes: 10 additions & 1 deletion libpod/sql_state_internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"path/filepath"
"time"

"github.com/containers/storage"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -266,7 +267,7 @@ type scannable interface {
}

// Read a single container from a single row result in the database
func ctrFromScannable(row scannable, runtime *Runtime, specsDir string) (*Container, error) {
func ctrFromScannable(row scannable, runtime *Runtime, specsDir string, lockDir string) (*Container, error) {
var (
id string
name string
Expand Down Expand Up @@ -384,6 +385,14 @@ func ctrFromScannable(row scannable, runtime *Runtime, specsDir string) (*Contai
ctr.valid = true
ctr.runtime = runtime

// Open and set the lockfile
lockPath := filepath.Join(lockDir, id)
lock, err := storage.GetLockfile(lockPath)
if err != nil {
return nil, errors.Wrapf(err, "error retrieving lockfile for container %s", id)
}
ctr.lock = lock

// Retrieve the spec from disk
ociSpec := new(spec.Spec)
specPath := getSpecPath(specsDir, id)
Expand Down
Loading

0 comments on commit 12682aa

Please sign in to comment.