mirror of
https://github.com/containers/podman.git
synced 2025-06-21 01:19:15 +08:00
Implement pod operations in SQL state
Signed-off-by: Matthew Heon <matthew.heon@gmail.com> Closes: #268 Approved by: rhatdan
This commit is contained in:
@ -3,7 +3,6 @@ package libpod
|
|||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@ -148,23 +147,7 @@ func (s *SQLState) Refresh() (err error) {
|
|||||||
|
|
||||||
// Container retrieves a container from its full ID
|
// Container retrieves a container from its full ID
|
||||||
func (s *SQLState) Container(id string) (*Container, error) {
|
func (s *SQLState) Container(id string) (*Container, error) {
|
||||||
const query = `SELECT containers.*,
|
const query = containerQuery + "WHERE containers.Id=?;"
|
||||||
containerState.State,
|
|
||||||
containerState.ConfigPath,
|
|
||||||
containerState.RunDir,
|
|
||||||
containerState.MountPoint,
|
|
||||||
containerState.StartedTime,
|
|
||||||
containerState.FinishedTime,
|
|
||||||
containerState.ExitCode,
|
|
||||||
containerState.OomKilled,
|
|
||||||
containerState.Pid,
|
|
||||||
containerState.NetNSPath,
|
|
||||||
containerState.IPAddress,
|
|
||||||
containerState.SubnetMask
|
|
||||||
FROM containers
|
|
||||||
INNER JOIN
|
|
||||||
containerState ON containers.Id = containerState.Id
|
|
||||||
WHERE containers.Id=?;`
|
|
||||||
|
|
||||||
if id == "" {
|
if id == "" {
|
||||||
return nil, ErrEmptyID
|
return nil, ErrEmptyID
|
||||||
@ -186,23 +169,7 @@ func (s *SQLState) Container(id string) (*Container, error) {
|
|||||||
|
|
||||||
// LookupContainer retrieves a container by full or unique partial ID or name
|
// LookupContainer retrieves a container by full or unique partial ID or name
|
||||||
func (s *SQLState) LookupContainer(idOrName string) (*Container, error) {
|
func (s *SQLState) LookupContainer(idOrName string) (*Container, error) {
|
||||||
const query = `SELECT containers.*,
|
const query = containerQuery + "WHERE (containers.Id LIKE ?) OR containers.Name=?;"
|
||||||
containerState.State,
|
|
||||||
containerState.ConfigPath,
|
|
||||||
containerState.RunDir,
|
|
||||||
containerState.MountPoint,
|
|
||||||
containerState.StartedTime,
|
|
||||||
containerState.FinishedTime,
|
|
||||||
containerState.ExitCode,
|
|
||||||
containerState.OomKilled,
|
|
||||||
containerState.Pid,
|
|
||||||
containerState.NetNSPath,
|
|
||||||
containerState.IPAddress,
|
|
||||||
containerState.SubnetMask
|
|
||||||
FROM containers
|
|
||||||
INNER JOIN
|
|
||||||
containerState ON containers.Id = containerState.Id
|
|
||||||
WHERE (containers.Id LIKE ?) OR containers.Name=?;`
|
|
||||||
|
|
||||||
if idOrName == "" {
|
if idOrName == "" {
|
||||||
return nil, ErrEmptyID
|
return nil, ErrEmptyID
|
||||||
@ -277,189 +244,15 @@ func (s *SQLState) HasContainer(id string) (bool, error) {
|
|||||||
// If the container belongs to a pod, that pod must already be present in the
|
// If the container belongs to a pod, that pod must already be present in the
|
||||||
// state, and the container will be added to the pod
|
// state, and the container will be added to the pod
|
||||||
func (s *SQLState) AddContainer(ctr *Container) (err error) {
|
func (s *SQLState) AddContainer(ctr *Container) (err error) {
|
||||||
const (
|
|
||||||
addCtr = `INSERT INTO containers VALUES (
|
|
||||||
?, ?, ?, ?, ?,
|
|
||||||
?, ?, ?, ?, ?,
|
|
||||||
?, ?, ?, ?, ?,
|
|
||||||
?, ?, ?, ?, ?,
|
|
||||||
?, ?, ?, ?, ?,
|
|
||||||
?, ?, ?, ?, ?,
|
|
||||||
?, ?, ?, ?, ?
|
|
||||||
);`
|
|
||||||
addCtrState = `INSERT INTO containerState VALUES (
|
|
||||||
?, ?, ?, ?, ?,
|
|
||||||
?, ?, ?, ?, ?,
|
|
||||||
?, ?, ?
|
|
||||||
);`
|
|
||||||
)
|
|
||||||
|
|
||||||
if !s.valid {
|
|
||||||
return ErrDBClosed
|
|
||||||
}
|
|
||||||
|
|
||||||
if !ctr.valid {
|
if !ctr.valid {
|
||||||
return ErrCtrRemoved
|
return ErrCtrRemoved
|
||||||
}
|
}
|
||||||
|
|
||||||
mounts, err := json.Marshal(ctr.config.Mounts)
|
if ctr.config.Pod != "" {
|
||||||
if err != nil {
|
return errors.Wrapf(ErrPodExists, "cannot add container that belongs to a pod, use AddContainerToPod instead")
|
||||||
return errors.Wrapf(err, "error marshaling container %s mounts to JSON", ctr.ID())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dnsServerJSON, err := json.Marshal(ctr.config.DNSServer)
|
return s.addContainer(ctr)
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "error marshaling container %s DNS servers to JSON", ctr.ID())
|
|
||||||
}
|
|
||||||
|
|
||||||
dnsSearchJSON, err := json.Marshal(ctr.config.DNSSearch)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "error marshaling container %s DNS search domains to JSON", ctr.ID())
|
|
||||||
}
|
|
||||||
|
|
||||||
dnsOptionJSON, err := json.Marshal(ctr.config.DNSOption)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "error marshaling container %s DNS options to JSON", ctr.ID())
|
|
||||||
}
|
|
||||||
|
|
||||||
hostAddJSON, err := json.Marshal(ctr.config.HostAdd)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "error marshaling container %s hosts to JSON", ctr.ID())
|
|
||||||
}
|
|
||||||
|
|
||||||
labelsJSON, err := json.Marshal(ctr.config.Labels)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "error marshaling container %s labels to JSON", ctr.ID())
|
|
||||||
}
|
|
||||||
|
|
||||||
netNSPath := ""
|
|
||||||
if ctr.state.NetNS != nil {
|
|
||||||
netNSPath = ctr.state.NetNS.Path()
|
|
||||||
}
|
|
||||||
|
|
||||||
specJSON, err := json.Marshal(ctr.config.Spec)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "error marshalling container %s spec to JSON", ctr.ID())
|
|
||||||
}
|
|
||||||
|
|
||||||
portsJSON := []byte{}
|
|
||||||
if len(ctr.config.PortMappings) > 0 {
|
|
||||||
portsJSON, err = json.Marshal(&ctr.config.PortMappings)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "error marshalling container %s port mappings to JSON", ctr.ID())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tx, err := s.db.Begin()
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "error beginning database transaction")
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
if err2 := tx.Rollback(); err2 != nil {
|
|
||||||
logrus.Errorf("Error rolling back transaction to add container %s: %v", ctr.ID(), err2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Add static container information
|
|
||||||
_, err = tx.Exec(addCtr,
|
|
||||||
ctr.ID(),
|
|
||||||
ctr.Name(),
|
|
||||||
stringToNullString(ctr.PodID()),
|
|
||||||
|
|
||||||
ctr.config.RootfsImageID,
|
|
||||||
ctr.config.RootfsImageName,
|
|
||||||
boolToSQL(ctr.config.ImageVolumes),
|
|
||||||
boolToSQL(ctr.config.ReadOnly),
|
|
||||||
ctr.config.ShmDir,
|
|
||||||
ctr.config.ShmSize,
|
|
||||||
ctr.config.StaticDir,
|
|
||||||
string(mounts),
|
|
||||||
ctr.LogPath(),
|
|
||||||
|
|
||||||
boolToSQL(ctr.config.Privileged),
|
|
||||||
boolToSQL(ctr.config.NoNewPrivs),
|
|
||||||
ctr.config.ProcessLabel,
|
|
||||||
ctr.config.MountLabel,
|
|
||||||
ctr.config.User,
|
|
||||||
|
|
||||||
stringToNullString(ctr.config.IPCNsCtr),
|
|
||||||
stringToNullString(ctr.config.MountNsCtr),
|
|
||||||
stringToNullString(ctr.config.NetNsCtr),
|
|
||||||
stringToNullString(ctr.config.PIDNsCtr),
|
|
||||||
stringToNullString(ctr.config.UserNsCtr),
|
|
||||||
stringToNullString(ctr.config.UTSNsCtr),
|
|
||||||
stringToNullString(ctr.config.CgroupNsCtr),
|
|
||||||
|
|
||||||
boolToSQL(ctr.config.CreateNetNS),
|
|
||||||
string(dnsServerJSON),
|
|
||||||
string(dnsSearchJSON),
|
|
||||||
string(dnsOptionJSON),
|
|
||||||
string(hostAddJSON),
|
|
||||||
|
|
||||||
boolToSQL(ctr.config.Stdin),
|
|
||||||
string(labelsJSON),
|
|
||||||
ctr.config.StopSignal,
|
|
||||||
ctr.config.StopTimeout,
|
|
||||||
timeToSQL(ctr.config.CreatedTime),
|
|
||||||
ctr.config.CgroupParent)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "error adding static information for container %s to database", ctr.ID())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add container state to the database
|
|
||||||
_, err = tx.Exec(addCtrState,
|
|
||||||
ctr.ID(),
|
|
||||||
ctr.state.State,
|
|
||||||
ctr.state.ConfigPath,
|
|
||||||
ctr.state.RunDir,
|
|
||||||
ctr.state.Mountpoint,
|
|
||||||
timeToSQL(ctr.state.StartedTime),
|
|
||||||
timeToSQL(ctr.state.FinishedTime),
|
|
||||||
ctr.state.ExitCode,
|
|
||||||
boolToSQL(ctr.state.OOMKilled),
|
|
||||||
ctr.state.PID,
|
|
||||||
netNSPath,
|
|
||||||
ctr.state.IPAddress,
|
|
||||||
ctr.state.SubnetMask)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "error adding container %s state to database", ctr.ID())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the container's runtime spec to disk
|
|
||||||
specPath := getSpecPath(s.specsDir, ctr.ID())
|
|
||||||
if err := ioutil.WriteFile(specPath, specJSON, 0750); err != nil {
|
|
||||||
return errors.Wrapf(err, "error saving container %s spec JSON to disk", ctr.ID())
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
if err2 := os.Remove(specPath); err2 != nil {
|
|
||||||
logrus.Errorf("Error removing container %s JSON spec from state: %v", ctr.ID(), err2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// If the container has port mappings, save them to disk
|
|
||||||
if len(ctr.config.PortMappings) > 0 {
|
|
||||||
portPath := getPortsPath(s.specsDir, ctr.ID())
|
|
||||||
if err := ioutil.WriteFile(portPath, portsJSON, 0750); err != nil {
|
|
||||||
return errors.Wrapf(err, "error saving container %s port JSON to disk", ctr.ID())
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
if err2 := os.Remove(portPath); err2 != nil {
|
|
||||||
logrus.Errorf("Error removing container %s JSON ports from state: %v", ctr.ID(), err2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := tx.Commit(); err != nil {
|
|
||||||
return errors.Wrapf(err, "error committing transaction to add container %s", ctr.ID())
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateContainer updates a container's state from the database
|
// UpdateContainer updates a container's state from the database
|
||||||
@ -709,93 +502,18 @@ func (s *SQLState) ContainerInUse(ctr *Container) ([]string, error) {
|
|||||||
return ids, nil
|
return ids, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveContainer removes the container from the state
|
// RemoveContainer removes the given container from the state
|
||||||
func (s *SQLState) RemoveContainer(ctr *Container) error {
|
func (s *SQLState) RemoveContainer(ctr *Container) error {
|
||||||
const (
|
if ctr.config.Pod != "" {
|
||||||
removeCtr = "DELETE FROM containers WHERE Id=?;"
|
return errors.Wrapf(ErrPodExists, "container %s belongs to a pod, use RemoveContainerFromPod", ctr.ID())
|
||||||
removeState = "DELETE FROM containerState WHERE ID=?;"
|
|
||||||
)
|
|
||||||
|
|
||||||
if !s.valid {
|
|
||||||
return ErrDBClosed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
committed := false
|
return s.removeContainer(ctr)
|
||||||
|
|
||||||
tx, err := s.db.Begin()
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "error beginning database transaction")
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err != nil && !committed {
|
|
||||||
if err2 := tx.Rollback(); err2 != nil {
|
|
||||||
logrus.Errorf("Error rolling back transaction to add container %s: %v", ctr.ID(), err2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Check rows acted on for the first transaction, verify we actually removed something
|
|
||||||
result, err := tx.Exec(removeCtr, ctr.ID())
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "error removing container %s from containers table", ctr.ID())
|
|
||||||
}
|
|
||||||
rows, err := result.RowsAffected()
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "error retrieving number of rows in transaction removing container %s", ctr.ID())
|
|
||||||
} else if rows == 0 {
|
|
||||||
return ErrNoSuchCtr
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := tx.Exec(removeState, ctr.ID()); err != nil {
|
|
||||||
return errors.Wrapf(err, "error removing container %s from state table", ctr.ID())
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := tx.Commit(); err != nil {
|
|
||||||
return errors.Wrapf(err, "error committing transaction to remove container %s", ctr.ID())
|
|
||||||
}
|
|
||||||
|
|
||||||
committed = true
|
|
||||||
|
|
||||||
// Remove the container's JSON from disk
|
|
||||||
jsonPath := getSpecPath(s.specsDir, ctr.ID())
|
|
||||||
if err := os.Remove(jsonPath); err != nil {
|
|
||||||
return errors.Wrapf(err, "error removing JSON spec from state for container %s", ctr.ID())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove containers ports JSON from disk
|
|
||||||
// May not exist, so ignore os.IsNotExist
|
|
||||||
portsPath := getPortsPath(s.specsDir, ctr.ID())
|
|
||||||
if err := os.Remove(portsPath); err != nil {
|
|
||||||
if !os.IsNotExist(err) {
|
|
||||||
return errors.Wrapf(err, "error removing JSON ports from state for container %s", ctr.ID())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctr.valid = false
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AllContainers retrieves all the containers presently in the state
|
// AllContainers retrieves all the containers presently in the state
|
||||||
func (s *SQLState) AllContainers() ([]*Container, error) {
|
func (s *SQLState) AllContainers() ([]*Container, error) {
|
||||||
// TODO maybe do an ORDER BY here?
|
const query = containerQuery + ";"
|
||||||
const query = `SELECT containers.*,
|
|
||||||
containerState.State,
|
|
||||||
containerState.ConfigPath,
|
|
||||||
containerState.RunDir,
|
|
||||||
containerState.MountPoint,
|
|
||||||
containerState.StartedTime,
|
|
||||||
containerState.FinishedTime,
|
|
||||||
containerState.ExitCode,
|
|
||||||
containerState.OomKilled,
|
|
||||||
containerState.Pid,
|
|
||||||
containerState.NetNSPath,
|
|
||||||
containerState.IPAddress,
|
|
||||||
containerState.SubnetMask
|
|
||||||
FROM containers
|
|
||||||
INNER JOIN
|
|
||||||
containerState ON containers.Id = containerState.Id
|
|
||||||
ORDER BY containers.CreatedTime DESC;`
|
|
||||||
|
|
||||||
if !s.valid {
|
if !s.valid {
|
||||||
return nil, ErrDBClosed
|
return nil, ErrDBClosed
|
||||||
@ -826,59 +544,352 @@ func (s *SQLState) AllContainers() ([]*Container, error) {
|
|||||||
|
|
||||||
// Pod retrieves a pod by its full ID
|
// Pod retrieves a pod by its full ID
|
||||||
func (s *SQLState) Pod(id string) (*Pod, error) {
|
func (s *SQLState) Pod(id string) (*Pod, error) {
|
||||||
return nil, ErrNotImplemented
|
const query = "SELECT * FROM pods WHERE Id=?;"
|
||||||
|
|
||||||
|
if !s.valid {
|
||||||
|
return nil, ErrDBClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
if id == "" {
|
||||||
|
return nil, ErrEmptyID
|
||||||
|
}
|
||||||
|
|
||||||
|
row := s.db.QueryRow(query, id)
|
||||||
|
|
||||||
|
pod, err := s.podFromScannable(row)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error retrieving pod %s from database", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pod, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LookupPod retrieves a pot by full or unique partial ID or name
|
// LookupPod retrieves a pot by full or unique partial ID or name
|
||||||
func (s *SQLState) LookupPod(idOrName string) (*Pod, error) {
|
func (s *SQLState) LookupPod(idOrName string) (*Pod, error) {
|
||||||
return nil, ErrNotImplemented
|
const query = "SELECT * FROM pods WHERE (Id LIKE ?) OR Name=?;"
|
||||||
|
|
||||||
|
if idOrName == "" {
|
||||||
|
return nil, ErrEmptyID
|
||||||
|
}
|
||||||
|
|
||||||
|
if !s.valid {
|
||||||
|
return nil, ErrDBClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := s.db.Query(query, idOrName+"%", idOrName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error retrieving pod %s row from database", idOrName)
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
foundResult := false
|
||||||
|
var pod *Pod
|
||||||
|
for rows.Next() {
|
||||||
|
if foundResult {
|
||||||
|
return nil, errors.Wrapf(ErrCtrExists, "more than one result for ID or name %s", idOrName)
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
pod, err = s.podFromScannable(rows)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error retrieving pod %s from database", idOrName)
|
||||||
|
}
|
||||||
|
foundResult = true
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error retrieving rows for pod ID or name %s", idOrName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !foundResult {
|
||||||
|
return nil, errors.Wrapf(ErrNoSuchCtr, "no pod with ID or name %s found", idOrName)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return pod, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasPod checks if a pod exists given its full ID
|
// HasPod checks if a pod exists given its full ID
|
||||||
func (s *SQLState) HasPod(id string) (bool, error) {
|
func (s *SQLState) HasPod(id string) (bool, error) {
|
||||||
return false, ErrNotImplemented
|
if id == "" {
|
||||||
|
return false, ErrEmptyID
|
||||||
|
}
|
||||||
|
|
||||||
|
if !s.valid {
|
||||||
|
return false, ErrDBClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.podExists(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PodHasContainer checks if the given pod containers a container with the given
|
// PodHasContainer checks if the given pod containers a container with the given
|
||||||
// ID
|
// ID
|
||||||
func (s *SQLState) PodHasContainer(pod *Pod, ctrID string) (bool, error) {
|
func (s *SQLState) PodHasContainer(pod *Pod, ctrID string) (bool, error) {
|
||||||
return false, ErrNotImplemented
|
const query = "SELECT 1 FROM containers WHERE Id=? AND Pod=?;"
|
||||||
|
|
||||||
|
if ctrID == "" {
|
||||||
|
return false, ErrEmptyID
|
||||||
|
}
|
||||||
|
|
||||||
|
if !s.valid {
|
||||||
|
return false, ErrDBClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
if !pod.valid {
|
||||||
|
return false, ErrPodRemoved
|
||||||
|
}
|
||||||
|
|
||||||
|
row := s.db.QueryRow(query, ctrID, pod.ID())
|
||||||
|
|
||||||
|
var check int
|
||||||
|
err := row.Scan(&check)
|
||||||
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, errors.Wrapf(err, "error questing database for existence of container %s", ctrID)
|
||||||
|
} else if check != 1 {
|
||||||
|
return false, errors.Wrapf(ErrInternal, "check digit for PodHasContainer query incorrect")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PodContainersByID returns the container IDs of all containers in the given
|
// PodContainersByID returns the container IDs of all containers in the given
|
||||||
// pod
|
// pod
|
||||||
func (s *SQLState) PodContainersByID(pod *Pod) ([]string, error) {
|
func (s *SQLState) PodContainersByID(pod *Pod) ([]string, error) {
|
||||||
return nil, ErrNotImplemented
|
const query = "SELECT Id FROM containers WHERE Pod=?;"
|
||||||
|
|
||||||
|
if !s.valid {
|
||||||
|
return nil, ErrDBClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
if !pod.valid {
|
||||||
|
return nil, ErrPodRemoved
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check to make sure pod still exists in DB
|
||||||
|
exists, err := s.podExists(pod.ID())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
pod.valid = false
|
||||||
|
return nil, ErrPodRemoved
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get actual containers
|
||||||
|
rows, err := s.db.Query(query, pod.ID())
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error retrieving containers from database")
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
containers := []string{}
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var id string
|
||||||
|
if err := rows.Scan(&id); err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return nil, ErrNoSuchCtr
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.Wrapf(err, "error parsing database row into container ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
containers = append(containers, id)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error retrieving container rows")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return containers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PodContainers returns all the containers in a pod given the pod's full ID
|
// PodContainers returns all the containers in a pod given the pod's full ID
|
||||||
func (s *SQLState) PodContainers(pod *Pod) ([]*Container, error) {
|
func (s *SQLState) PodContainers(pod *Pod) ([]*Container, error) {
|
||||||
return nil, ErrNotImplemented
|
const query = containerQuery + "WHERE containers.Pod=?;"
|
||||||
|
|
||||||
|
if !s.valid {
|
||||||
|
return nil, ErrDBClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
if !pod.valid {
|
||||||
|
return nil, ErrPodRemoved
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check to make sure pod still exists in DB
|
||||||
|
exists, err := s.podExists(pod.ID())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
pod.valid = false
|
||||||
|
return nil, ErrPodRemoved
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get actual containers
|
||||||
|
rows, err := s.db.Query(query, pod.ID())
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error retrieving containers from database")
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
containers := []*Container{}
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
ctr, err := s.ctrFromScannable(rows)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
containers = append(containers, ctr)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error retrieving container rows")
|
||||||
|
}
|
||||||
|
|
||||||
|
return containers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddPod adds a pod to the state
|
// AddPod adds a pod to the state
|
||||||
// Only empty pods can be added to the state
|
// Only empty pods can be added to the state
|
||||||
func (s *SQLState) AddPod(pod *Pod) error {
|
func (s *SQLState) AddPod(pod *Pod) (err error) {
|
||||||
return ErrNotImplemented
|
const query = "INSERT INTO pods VALUES (?, ?, ?);"
|
||||||
|
|
||||||
|
if !s.valid {
|
||||||
|
return ErrDBClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
if !pod.valid {
|
||||||
|
return ErrPodRemoved
|
||||||
|
}
|
||||||
|
|
||||||
|
labelsJSON, err := json.Marshal(pod.labels)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error marshaling pod %s labels to JSON", pod.ID())
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := s.db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error beginning database transaction")
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
if err2 := tx.Rollback(); err2 != nil {
|
||||||
|
logrus.Errorf("Error rolling back transaction to add pod %s: %v", pod.ID(), err2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
_, err = tx.Exec(query, pod.ID(), pod.Name(), string(labelsJSON))
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error adding pod %s to database", pod.ID())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Commit(); err != nil {
|
||||||
|
return errors.Wrapf(err, "error committing transaction to add pod %s", pod.ID())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemovePod removes a pod from the state
|
// RemovePod removes a pod from the state
|
||||||
// Only empty pods can be removed
|
// Only empty pods can be removed
|
||||||
func (s *SQLState) RemovePod(pod *Pod) error {
|
func (s *SQLState) RemovePod(pod *Pod) error {
|
||||||
return ErrNotImplemented
|
const query = "DELETE FROM pods WHERE ID=?;"
|
||||||
|
|
||||||
|
if !s.valid {
|
||||||
|
return ErrDBClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := s.db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error beginning database transaction")
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
if err2 := tx.Rollback(); err2 != nil {
|
||||||
|
logrus.Errorf("Error rolling back transaction to remove pod %s: %v", pod.ID(), err2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Check rows acted on for the first transaction, verify we actually removed something
|
||||||
|
result, err := tx.Exec(query, pod.ID())
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error removing pod %s from containers table", pod.ID())
|
||||||
|
}
|
||||||
|
rows, err := result.RowsAffected()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error retrieving number of rows in transaction removing pod %s", pod.ID())
|
||||||
|
} else if rows == 0 {
|
||||||
|
return ErrNoSuchPod
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Commit(); err != nil {
|
||||||
|
return errors.Wrapf(err, "error committing transaction to remove pod %s", pod.ID())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddContainerToPod adds a container to the given pod
|
// AddContainerToPod adds a container to the given pod
|
||||||
func (s *SQLState) AddContainerToPod(pod *Pod, ctr *Container) error {
|
func (s *SQLState) AddContainerToPod(pod *Pod, ctr *Container) error {
|
||||||
return ErrNotImplemented
|
if !pod.valid {
|
||||||
|
return ErrPodRemoved
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ctr.valid {
|
||||||
|
return ErrCtrRemoved
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctr.config.Pod != pod.ID() {
|
||||||
|
return errors.Wrapf(ErrInvalidArg, "container's pod ID does not match given pod's ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.addContainer(ctr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveContainerFromPod removes a container from the given pod
|
// RemoveContainerFromPod removes a container from the given pod
|
||||||
func (s *SQLState) RemoveContainerFromPod(pod *Pod, ctr *Container) error {
|
func (s *SQLState) RemoveContainerFromPod(pod *Pod, ctr *Container) error {
|
||||||
return ErrNotImplemented
|
if ctr.config.Pod != pod.ID() {
|
||||||
|
return errors.Wrapf(ErrInvalidArg, "container %s is not in pod %s", ctr.ID(), pod.ID())
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.removeContainer(ctr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AllPods retrieves all pods presently in the state
|
// AllPods retrieves all pods presently in the state
|
||||||
func (s *SQLState) AllPods() ([]*Pod, error) {
|
func (s *SQLState) AllPods() ([]*Pod, error) {
|
||||||
return nil, ErrNotImplemented
|
const query = "SELECT * FROM pods;"
|
||||||
|
|
||||||
|
if !s.valid {
|
||||||
|
return nil, ErrDBClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := s.db.Query(query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error querying database for all pods")
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
pods := []*Pod{}
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
pod, err := s.podFromScannable(rows)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pods = append(pods, pod)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error retrieving pod rows")
|
||||||
|
}
|
||||||
|
|
||||||
|
return pods, nil
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,28 @@ import (
|
|||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Basic structure of a query to retrieve a container
|
||||||
|
// Just append optional WHERE clauses and end with a semicolon
|
||||||
|
// Contains trailing whitespace to ensure we can append without issue
|
||||||
|
containerQuery = `SELECT containers.*,
|
||||||
|
containerState.State,
|
||||||
|
containerState.ConfigPath,
|
||||||
|
containerState.RunDir,
|
||||||
|
containerState.MountPoint,
|
||||||
|
containerState.StartedTime,
|
||||||
|
containerState.FinishedTime,
|
||||||
|
containerState.ExitCode,
|
||||||
|
containerState.OomKilled,
|
||||||
|
containerState.Pid,
|
||||||
|
containerState.NetNSPath,
|
||||||
|
containerState.IPAddress,
|
||||||
|
containerState.SubnetMask
|
||||||
|
FROM containers
|
||||||
|
INNER JOIN
|
||||||
|
containerState ON containers.Id = containerState.Id `
|
||||||
|
)
|
||||||
|
|
||||||
// Checks that the DB configuration matches the runtime's configuration
|
// Checks that the DB configuration matches the runtime's configuration
|
||||||
func checkDB(db *sql.DB, r *Runtime) (err error) {
|
func checkDB(db *sql.DB, r *Runtime) (err error) {
|
||||||
// Create a table to hold runtime information
|
// Create a table to hold runtime information
|
||||||
@ -216,7 +238,7 @@ func prepareDB(db *sql.DB) (err error) {
|
|||||||
CHECK (Stdin IN (0, 1)),
|
CHECK (Stdin IN (0, 1)),
|
||||||
CHECK (StopSignal>=0),
|
CHECK (StopSignal>=0),
|
||||||
FOREIGN KEY (Id) REFERENCES containerState(Id) DEFERRABLE INITIALLY DEFERRED
|
FOREIGN KEY (Id) REFERENCES containerState(Id) DEFERRABLE INITIALLY DEFERRED
|
||||||
FOREIGN KEY (Pod) REFERENCES pod(Id) DEFERRABLE INITIALLY DEFERRED,
|
FOREIGN KEY (Pod) REFERENCES pods(Id) DEFERRABLE INITIALLY DEFERRED,
|
||||||
FOREIGN KEY (IPCNsCtr) REFERENCES containers(Id) DEFERRABLE INITIALLY DEFERRED,
|
FOREIGN KEY (IPCNsCtr) REFERENCES containers(Id) DEFERRABLE INITIALLY DEFERRED,
|
||||||
FOREIGN KEY (MountNsCtr) REFERENCES containers(Id) DEFERRABLE INITIALLY DEFERRED,
|
FOREIGN KEY (MountNsCtr) REFERENCES containers(Id) DEFERRABLE INITIALLY DEFERRED,
|
||||||
FOREIGN KEY (NetNsCtr) REFERENCES containers(Id) DEFERRABLE INITIALLY DEFERRED,
|
FOREIGN KEY (NetNsCtr) REFERENCES containers(Id) DEFERRABLE INITIALLY DEFERRED,
|
||||||
@ -252,7 +274,7 @@ func prepareDB(db *sql.DB) (err error) {
|
|||||||
|
|
||||||
// Create a table for pod config
|
// Create a table for pod config
|
||||||
const createPod = `
|
const createPod = `
|
||||||
CREATE TABLE IF NOT EXISTS pod(
|
CREATE TABLE IF NOT EXISTS pods(
|
||||||
Id TEXT NOT NULL PRIMARY KEY,
|
Id TEXT NOT NULL PRIMARY KEY,
|
||||||
Name TEXT NOT NULL UNIQUE,
|
Name TEXT NOT NULL UNIQUE,
|
||||||
Labels TEXT NOT NULL
|
Labels TEXT NOT NULL
|
||||||
@ -290,6 +312,29 @@ func prepareDB(db *sql.DB) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if given pod exists
|
||||||
|
// Internal-only version of hasPod
|
||||||
|
func (s *SQLState) podExists(id string) (bool, error) {
|
||||||
|
const query = "SELECT 1 FROM pods WHERE Id=?;"
|
||||||
|
|
||||||
|
row := s.db.QueryRow(query, id)
|
||||||
|
|
||||||
|
var check int
|
||||||
|
err := row.Scan(&check)
|
||||||
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, errors.Wrapf(err, "error questing database for existence of pod %s", id)
|
||||||
|
} else if check != 1 {
|
||||||
|
return false, errors.Wrapf(ErrInternal, "check digit for podExists query incorrect")
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Get filename for OCI spec on disk
|
// Get filename for OCI spec on disk
|
||||||
func getSpecPath(specsDir, id string) string {
|
func getSpecPath(specsDir, id string) string {
|
||||||
return filepath.Join(specsDir, id)
|
return filepath.Join(specsDir, id)
|
||||||
@ -619,3 +664,296 @@ func (s *SQLState) ctrFromScannable(row scannable) (*Container, error) {
|
|||||||
|
|
||||||
return ctr, nil
|
return ctr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read a single pod from a single row result in the database
|
||||||
|
func (s *SQLState) podFromScannable(row scannable) (*Pod, error) {
|
||||||
|
var (
|
||||||
|
id string
|
||||||
|
name string
|
||||||
|
labelsJSON string
|
||||||
|
)
|
||||||
|
|
||||||
|
err := row.Scan(&id, &name, &labelsJSON)
|
||||||
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return nil, ErrNoSuchPod
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.Wrapf(err, "error parsing database row into pod")
|
||||||
|
}
|
||||||
|
|
||||||
|
pod := new(Pod)
|
||||||
|
pod.id = id
|
||||||
|
pod.name = name
|
||||||
|
pod.runtime = s.runtime
|
||||||
|
|
||||||
|
// Decode labels JSON
|
||||||
|
podLabels := make(map[string]string)
|
||||||
|
if err := json.Unmarshal([]byte(labelsJSON), &podLabels); err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error unmarshaling pod %s labels JSON", id)
|
||||||
|
}
|
||||||
|
pod.labels = podLabels
|
||||||
|
|
||||||
|
// Retrieve pod lock
|
||||||
|
// Open and set the lockfile
|
||||||
|
lockPath := filepath.Join(s.lockDir, id)
|
||||||
|
lock, err := storage.GetLockfile(lockPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error retrieving lockfile for pod %s", id)
|
||||||
|
}
|
||||||
|
pod.lock = lock
|
||||||
|
|
||||||
|
pod.valid = true
|
||||||
|
|
||||||
|
return pod, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal function for adding containers
|
||||||
|
func (s *SQLState) addContainer(ctr *Container) (err error) {
|
||||||
|
const (
|
||||||
|
addCtr = `INSERT INTO containers VALUES (
|
||||||
|
?, ?, ?, ?, ?,
|
||||||
|
?, ?, ?, ?, ?,
|
||||||
|
?, ?, ?, ?, ?,
|
||||||
|
?, ?, ?, ?, ?,
|
||||||
|
?, ?, ?, ?, ?,
|
||||||
|
?, ?, ?, ?, ?,
|
||||||
|
?, ?, ?, ?, ?
|
||||||
|
);`
|
||||||
|
addCtrState = `INSERT INTO containerState VALUES (
|
||||||
|
?, ?, ?, ?, ?,
|
||||||
|
?, ?, ?, ?, ?,
|
||||||
|
?, ?, ?
|
||||||
|
);`
|
||||||
|
)
|
||||||
|
|
||||||
|
if !s.valid {
|
||||||
|
return ErrDBClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
mounts, err := json.Marshal(ctr.config.Mounts)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error marshaling container %s mounts to JSON", ctr.ID())
|
||||||
|
}
|
||||||
|
|
||||||
|
dnsServerJSON, err := json.Marshal(ctr.config.DNSServer)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error marshaling container %s DNS servers to JSON", ctr.ID())
|
||||||
|
}
|
||||||
|
|
||||||
|
dnsSearchJSON, err := json.Marshal(ctr.config.DNSSearch)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error marshaling container %s DNS search domains to JSON", ctr.ID())
|
||||||
|
}
|
||||||
|
|
||||||
|
dnsOptionJSON, err := json.Marshal(ctr.config.DNSOption)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error marshaling container %s DNS options to JSON", ctr.ID())
|
||||||
|
}
|
||||||
|
|
||||||
|
hostAddJSON, err := json.Marshal(ctr.config.HostAdd)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error marshaling container %s hosts to JSON", ctr.ID())
|
||||||
|
}
|
||||||
|
|
||||||
|
labelsJSON, err := json.Marshal(ctr.config.Labels)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error marshaling container %s labels to JSON", ctr.ID())
|
||||||
|
}
|
||||||
|
|
||||||
|
netNSPath := ""
|
||||||
|
if ctr.state.NetNS != nil {
|
||||||
|
netNSPath = ctr.state.NetNS.Path()
|
||||||
|
}
|
||||||
|
|
||||||
|
specJSON, err := json.Marshal(ctr.config.Spec)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error marshalling container %s spec to JSON", ctr.ID())
|
||||||
|
}
|
||||||
|
|
||||||
|
portsJSON := []byte{}
|
||||||
|
if len(ctr.config.PortMappings) > 0 {
|
||||||
|
portsJSON, err = json.Marshal(&ctr.config.PortMappings)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error marshalling container %s port mappings to JSON", ctr.ID())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := s.db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error beginning database transaction")
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
if err2 := tx.Rollback(); err2 != nil {
|
||||||
|
logrus.Errorf("Error rolling back transaction to add container %s: %v", ctr.ID(), err2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Add static container information
|
||||||
|
_, err = tx.Exec(addCtr,
|
||||||
|
ctr.ID(),
|
||||||
|
ctr.Name(),
|
||||||
|
stringToNullString(ctr.PodID()),
|
||||||
|
|
||||||
|
ctr.config.RootfsImageID,
|
||||||
|
ctr.config.RootfsImageName,
|
||||||
|
boolToSQL(ctr.config.ImageVolumes),
|
||||||
|
boolToSQL(ctr.config.ReadOnly),
|
||||||
|
ctr.config.ShmDir,
|
||||||
|
ctr.config.ShmSize,
|
||||||
|
ctr.config.StaticDir,
|
||||||
|
string(mounts),
|
||||||
|
ctr.config.LogPath,
|
||||||
|
|
||||||
|
boolToSQL(ctr.config.Privileged),
|
||||||
|
boolToSQL(ctr.config.NoNewPrivs),
|
||||||
|
ctr.config.ProcessLabel,
|
||||||
|
ctr.config.MountLabel,
|
||||||
|
ctr.config.User,
|
||||||
|
|
||||||
|
stringToNullString(ctr.config.IPCNsCtr),
|
||||||
|
stringToNullString(ctr.config.MountNsCtr),
|
||||||
|
stringToNullString(ctr.config.NetNsCtr),
|
||||||
|
stringToNullString(ctr.config.PIDNsCtr),
|
||||||
|
stringToNullString(ctr.config.UserNsCtr),
|
||||||
|
stringToNullString(ctr.config.UTSNsCtr),
|
||||||
|
stringToNullString(ctr.config.CgroupNsCtr),
|
||||||
|
|
||||||
|
boolToSQL(ctr.config.CreateNetNS),
|
||||||
|
string(dnsServerJSON),
|
||||||
|
string(dnsSearchJSON),
|
||||||
|
string(dnsOptionJSON),
|
||||||
|
string(hostAddJSON),
|
||||||
|
|
||||||
|
boolToSQL(ctr.config.Stdin),
|
||||||
|
string(labelsJSON),
|
||||||
|
ctr.config.StopSignal,
|
||||||
|
ctr.config.StopTimeout,
|
||||||
|
timeToSQL(ctr.config.CreatedTime),
|
||||||
|
ctr.config.CgroupParent)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error adding static information for container %s to database", ctr.ID())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add container state to the database
|
||||||
|
_, err = tx.Exec(addCtrState,
|
||||||
|
ctr.ID(),
|
||||||
|
ctr.state.State,
|
||||||
|
ctr.state.ConfigPath,
|
||||||
|
ctr.state.RunDir,
|
||||||
|
ctr.state.Mountpoint,
|
||||||
|
timeToSQL(ctr.state.StartedTime),
|
||||||
|
timeToSQL(ctr.state.FinishedTime),
|
||||||
|
ctr.state.ExitCode,
|
||||||
|
boolToSQL(ctr.state.OOMKilled),
|
||||||
|
ctr.state.PID,
|
||||||
|
netNSPath,
|
||||||
|
ctr.state.IPAddress,
|
||||||
|
ctr.state.SubnetMask)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error adding container %s state to database", ctr.ID())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the container's runtime spec to disk
|
||||||
|
specPath := getSpecPath(s.specsDir, ctr.ID())
|
||||||
|
if err := ioutil.WriteFile(specPath, specJSON, 0750); err != nil {
|
||||||
|
return errors.Wrapf(err, "error saving container %s spec JSON to disk", ctr.ID())
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
if err2 := os.Remove(specPath); err2 != nil {
|
||||||
|
logrus.Errorf("Error removing container %s JSON spec from state: %v", ctr.ID(), err2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// If the container has port mappings, save them to disk
|
||||||
|
if len(ctr.config.PortMappings) > 0 {
|
||||||
|
portPath := getPortsPath(s.specsDir, ctr.ID())
|
||||||
|
if err := ioutil.WriteFile(portPath, portsJSON, 0750); err != nil {
|
||||||
|
return errors.Wrapf(err, "error saving container %s port JSON to disk", ctr.ID())
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
if err2 := os.Remove(portPath); err2 != nil {
|
||||||
|
logrus.Errorf("Error removing container %s JSON ports from state: %v", ctr.ID(), err2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Commit(); err != nil {
|
||||||
|
return errors.Wrapf(err, "error committing transaction to add container %s", ctr.ID())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal functions for removing containers
|
||||||
|
func (s *SQLState) removeContainer(ctr *Container) error {
|
||||||
|
const (
|
||||||
|
removeCtr = "DELETE FROM containers WHERE Id=?;"
|
||||||
|
removeState = "DELETE FROM containerState WHERE Id=?;"
|
||||||
|
)
|
||||||
|
|
||||||
|
if !s.valid {
|
||||||
|
return ErrDBClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
committed := false
|
||||||
|
|
||||||
|
tx, err := s.db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error beginning database transaction")
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil && !committed {
|
||||||
|
if err2 := tx.Rollback(); err2 != nil {
|
||||||
|
logrus.Errorf("Error rolling back transaction to add container %s: %v", ctr.ID(), err2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Check rows acted on for the first transaction, verify we actually removed something
|
||||||
|
result, err := tx.Exec(removeCtr, ctr.ID())
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error removing container %s from containers table", ctr.ID())
|
||||||
|
}
|
||||||
|
rows, err := result.RowsAffected()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error retrieving number of rows in transaction removing container %s", ctr.ID())
|
||||||
|
} else if rows == 0 {
|
||||||
|
return ErrNoSuchCtr
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := tx.Exec(removeState, ctr.ID()); err != nil {
|
||||||
|
return errors.Wrapf(err, "error removing container %s from state table", ctr.ID())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Commit(); err != nil {
|
||||||
|
return errors.Wrapf(err, "error committing transaction to remove container %s", ctr.ID())
|
||||||
|
}
|
||||||
|
|
||||||
|
committed = true
|
||||||
|
|
||||||
|
// Remove the container's JSON from disk
|
||||||
|
jsonPath := getSpecPath(s.specsDir, ctr.ID())
|
||||||
|
if err := os.Remove(jsonPath); err != nil {
|
||||||
|
return errors.Wrapf(err, "error removing JSON spec from state for container %s", ctr.ID())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove containers ports JSON from disk
|
||||||
|
// May not exist, so ignore os.IsNotExist
|
||||||
|
portsPath := getPortsPath(s.specsDir, ctr.ID())
|
||||||
|
if err := os.Remove(portsPath); err != nil {
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
return errors.Wrapf(err, "error removing JSON ports from state for container %s", ctr.ID())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctr.valid = false
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user