diff options
-rw-r--r-- | libpod/container.go | 31 | ||||
-rw-r--r-- | libpod/sql_state.go | 73 | ||||
-rw-r--r-- | libpod/sql_state_internal.go | 101 | ||||
-rw-r--r-- | libpod/sql_state_test.go | 20 |
4 files changed, 190 insertions, 35 deletions
diff --git a/libpod/container.go b/libpod/container.go index 2c769b00b..7b0852c07 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -156,23 +156,28 @@ type ContainerConfig struct { Mounts []string `json:"mounts,omitempty"` // Security Config + // Whether the container is privileged + Privileged bool `json:"privileged"` + // Whether to set the No New Privileges flag + NoNewPrivs bool `json:"noNewPrivs"` // SELinux process label for container ProcessLabel string `json:"ProcessLabel,omitempty"` // SELinux mount label for root filesystem MountLabel string `json:"MountLabel,omitempty"` // User and group to use in the container // Can be specified by name or UID/GID - User string `json:"user"` + User string `json:"user,omitempty"` // Namespace Config // IDs of container to share namespaces with // NetNsCtr conflicts with the CreateNetNS bool - IPCNsCtr string `json:"ipcNsCtr"` - MountNsCtr string `json:"mountNsCtr"` - NetNsCtr string `json:"netNsCtr"` - PIDNsCtr string `json:"pidNsCtr"` - UserNsCtr string `json:"userNsCtr"` - UTSNsCtr string `json:"utsNsCtr"` + IPCNsCtr string `json:"ipcNsCtr,omitempty"` + MountNsCtr string `json:"mountNsCtr,omitempty"` + NetNsCtr string `json:"netNsCtr,omitempty"` + PIDNsCtr string `json:"pidNsCtr,omitempty"` + UserNsCtr string `json:"userNsCtr,omitempty"` + UTSNsCtr string `json:"utsNsCtr,omitempty"` + CgroupNsCtr string `json:"cgroupNsCtr,omitempty"` // Network Config // CreateNetNS indicates that libpod should create and configure a new @@ -183,6 +188,18 @@ type ContainerConfig struct { // namespace // These are not used unless CreateNetNS is true PortMappings []ocicni.PortMapping `json:"portMappings,omitempty"` + // DNS servers to use in container resolv.conf + // Will override servers in host resolv if set + DNSServer []net.IP `json:"dnsServer,omitempty"` + // DNS Search domains to use in container resolv.conf + // Will override search domains in host resolv if set + DNSSearch []string `json:"dnsSearch,omitempty"` + // DNS options to be set in container resolv.conf + // With override options in host resolv if set + DNSOption []string `json:"dnsOption,omitempty"` + // Hosts to add in container + // Will be appended to host's host file + HostAdd []string `json:"hostsAdd,omitempty"` // Misc Options // Whether to keep container STDIN open diff --git a/libpod/sql_state.go b/libpod/sql_state.go index fe3232e62..51ec25510 100644 --- a/libpod/sql_state.go +++ b/libpod/sql_state.go @@ -15,7 +15,7 @@ import ( // DBSchema is the current DB schema version // Increments every time a change is made to the database's tables -const DBSchema = 7 +const DBSchema = 8 // SQLState is a state implementation backed by a persistent SQLite3 database type SQLState struct { @@ -284,7 +284,8 @@ func (s *SQLState) AddContainer(ctr *Container) (err error) { ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, - ?, ?, ? + ?, ?, ?, ?, ?, + ?, ?, ?, ? );` addCtrState = `INSERT INTO containerState VALUES ( ?, ?, ?, ?, ?, @@ -306,9 +307,24 @@ func (s *SQLState) AddContainer(ctr *Container) (err error) { return errors.Wrapf(err, "error marshaling container %s mounts to JSON", ctr.ID()) } - portsJSON, err := json.Marshal(ctr.config.PortMappings) + 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 port mappings to JSON", ctr.ID()) + 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) @@ -321,6 +337,19 @@ func (s *SQLState) AddContainer(ctr *Container) (err error) { 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") @@ -348,6 +377,8 @@ func (s *SQLState) AddContainer(ctr *Container) (err error) { ctr.config.StaticDir, string(mounts), + boolToSQL(ctr.config.Privileged), + boolToSQL(ctr.config.NoNewPrivs), ctr.config.ProcessLabel, ctr.config.MountLabel, ctr.config.User, @@ -358,9 +389,13 @@ func (s *SQLState) AddContainer(ctr *Container) (err error) { stringToNullString(ctr.config.PIDNsCtr), stringToNullString(ctr.config.UserNsCtr), stringToNullString(ctr.config.UTSNsCtr), + stringToNullString(ctr.config.CgroupNsCtr), boolToSQL(ctr.config.CreateNetNS), - string(portsJSON), + string(dnsServerJSON), + string(dnsSearchJSON), + string(dnsOptionJSON), + string(hostAddJSON), boolToSQL(ctr.config.Stdin), string(labelsJSON), @@ -392,10 +427,6 @@ func (s *SQLState) AddContainer(ctr *Container) (err error) { } // Save the container's runtime spec to disk - specJSON, err := json.Marshal(ctr.config.Spec) - if err != nil { - return errors.Wrapf(err, "error marshalling container %s spec to JSON", ctr.ID()) - } 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()) @@ -408,6 +439,21 @@ func (s *SQLState) AddContainer(ctr *Container) (err error) { } }() + // 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()) } @@ -668,6 +714,15 @@ func (s *SQLState) RemoveContainer(ctr *Container) error { 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 diff --git a/libpod/sql_state_internal.go b/libpod/sql_state_internal.go index ef3b6bd4e..189fae190 100644 --- a/libpod/sql_state_internal.go +++ b/libpod/sql_state_internal.go @@ -4,6 +4,7 @@ import ( "database/sql" "encoding/json" "io/ioutil" + "os" "path/filepath" "time" @@ -178,6 +179,8 @@ func prepareDB(db *sql.DB) (err error) { StaticDir TEXT NOT NULL, Mounts TEXT NOT NULL, + Privileged INTEGER NOT NULL, + NoNewPrivs INTEGER NOT NULL, ProcessLabel TEXT NOT NULL, MountLabel TEXT NOT NULL, User TEXT NOT NULL, @@ -188,9 +191,13 @@ func prepareDB(db *sql.DB) (err error) { PIDNsCtr TEXT, UserNsCtr TEXT, UTSNsCtr TEXT, + CgroupNsCtr TEXT, CreateNetNS INTEGER NOT NULL, - PortMappings TEXT NOT NULL, + DNSServer TEXT NOT NULL, + DNSSearch TEXT NOT NULL, + DNSOption TEXT NOT NULL, + HostAdd TEXT NOT NULL, Stdin INTEGER NOT NULL, LabelsJSON TEXT NOT NULL, @@ -202,16 +209,20 @@ func prepareDB(db *sql.DB) (err error) { CHECK (ImageVolumes IN (0, 1)), CHECK (ReadOnly IN (0, 1)), CHECK (SHMSize>=0), + CHECK (Privileged IN (0, 1)), + CHECK (NoNewPrivs IN (0, 1)), CHECK (CreateNetNS IN (0, 1)), CHECK (Stdin IN (0, 1)), CHECK (StopSignal>=0), - FOREIGN KEY (Pod) REFERENCES pod(Id) DEFERRABLE INITIALLY DEFERRED, - FOREIGN KEY (IPCNsCtr) 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 (PIDNsCtr) REFERENCES containers(Id) DEFERRABLE INITIALLY DEFERRED, - FOREIGN KEY (UserNsCtr) REFERENCES containers(Id) DEFERRABLE INITIALLY DEFERRED, - FOREIGN KEY (UTSNsCtr) REFERENCES containers(Id) DEFERRABLE INITIALLY DEFERRED + FOREIGN KEY (Id) REFERENCES containerState(Id) DEFERRABLE INITIALLY DEFERRED + FOREIGN KEY (Pod) REFERENCES pod(Id) DEFERRABLE INITIALLY DEFERRED, + FOREIGN KEY (IPCNsCtr) 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 (PIDNsCtr) REFERENCES containers(Id) DEFERRABLE INITIALLY DEFERRED, + FOREIGN KEY (UserNsCtr) REFERENCES containers(Id) DEFERRABLE INITIALLY DEFERRED, + FOREIGN KEY (UTSNsCtr) REFERENCES containers(Id) DEFERRABLE INITIALLY DEFERRED, + FOREIGN KEY (CgroupNsCtr) REFERENCES containers(Id) DEFERRABLE INITIALLY DEFERRED ); ` @@ -283,6 +294,11 @@ func getSpecPath(specsDir, id string) string { return filepath.Join(specsDir, id) } +// Get filename for container port mappings on disk +func getPortsPath(specsDir, id string) string { + return filepath.Join(specsDir, id+"_ports") +} + // Convert a bool into SQL-readable format func boolToSQL(b bool) int { if b { @@ -347,19 +363,25 @@ func (s *SQLState) ctrFromScannable(row scannable) (*Container, error) { staticDir string mounts string + privileged int + noNewPrivs int processLabel string mountLabel string user string - ipcNsCtrNullStr sql.NullString - mountNsCtrNullStr sql.NullString - netNsCtrNullStr sql.NullString - pidNsCtrNullStr sql.NullString - userNsCtrNullStr sql.NullString - utsNsCtrNullStr sql.NullString + ipcNsCtrNullStr sql.NullString + mountNsCtrNullStr sql.NullString + netNsCtrNullStr sql.NullString + pidNsCtrNullStr sql.NullString + userNsCtrNullStr sql.NullString + utsNsCtrNullStr sql.NullString + cgroupNsCtrNullStr sql.NullString - createNetNS int - portMappingsJSON string + createNetNS int + dnsServerJSON string + dnsSearchJSON string + dnsOptionJSON string + hostAddJSON string stdin int labelsJSON string @@ -396,6 +418,8 @@ func (s *SQLState) ctrFromScannable(row scannable) (*Container, error) { &staticDir, &mounts, + &privileged, + &noNewPrivs, &processLabel, &mountLabel, &user, @@ -406,9 +430,13 @@ func (s *SQLState) ctrFromScannable(row scannable) (*Container, error) { &pidNsCtrNullStr, &userNsCtrNullStr, &utsNsCtrNullStr, + &cgroupNsCtrNullStr, &createNetNS, - &portMappingsJSON, + &dnsServerJSON, + &dnsSearchJSON, + &dnsOptionJSON, + &hostAddJSON, &stdin, &labelsJSON, @@ -453,6 +481,8 @@ func (s *SQLState) ctrFromScannable(row scannable) (*Container, error) { ctr.config.ShmSize = shmSize ctr.config.StaticDir = staticDir + ctr.config.Privileged = boolFromSQL(privileged) + ctr.config.NoNewPrivs = boolFromSQL(noNewPrivs) ctr.config.ProcessLabel = processLabel ctr.config.MountLabel = mountLabel ctr.config.User = user @@ -463,6 +493,7 @@ func (s *SQLState) ctrFromScannable(row scannable) (*Container, error) { ctr.config.PIDNsCtr = stringFromNullString(pidNsCtrNullStr) ctr.config.UserNsCtr = stringFromNullString(userNsCtrNullStr) ctr.config.UTSNsCtr = stringFromNullString(utsNsCtrNullStr) + ctr.config.CgroupNsCtr = stringFromNullString(cgroupNsCtrNullStr) ctr.config.CreateNetNS = boolFromSQL(createNetNS) @@ -490,8 +521,20 @@ func (s *SQLState) ctrFromScannable(row scannable) (*Container, error) { return nil, errors.Wrapf(err, "error parsing container %s mounts JSON", id) } - if err := json.Unmarshal([]byte(portMappingsJSON), &ctr.config.PortMappings); err != nil { - return nil, errors.Wrapf(err, "error parsing container %s port mappings JSON", id) + if err := json.Unmarshal([]byte(dnsServerJSON), &ctr.config.DNSServer); err != nil { + return nil, errors.Wrapf(err, "error parsing container %s DNS server JSON", id) + } + + if err := json.Unmarshal([]byte(dnsSearchJSON), &ctr.config.DNSSearch); err != nil { + return nil, errors.Wrapf(err, "error parsing container %s DNS search JSON", id) + } + + if err := json.Unmarshal([]byte(dnsOptionJSON), &ctr.config.DNSOption); err != nil { + return nil, errors.Wrapf(err, "error parsing container %s DNS option JSON", id) + } + + if err := json.Unmarshal([]byte(hostAddJSON), &ctr.config.HostAdd); err != nil { + return nil, errors.Wrapf(err, "error parsing container %s DNS server JSON", id) } labels := make(map[string]string) @@ -550,5 +593,25 @@ func (s *SQLState) ctrFromScannable(row scannable) (*Container, error) { } ctr.config.Spec = ociSpec + // Retrieve the ports from disk + // They may not exist - if they don't, this container just doesn't have ports + portPath := getPortsPath(s.specsDir, id) + _, err = os.Stat(portPath) + if err != nil { + if !os.IsNotExist(err) { + return nil, errors.Wrapf(err, "error stating container %s JSON ports", id) + } + } + if err == nil { + // The file exists, read it + fileContents, err := ioutil.ReadFile(portPath) + if err != nil { + return nil, errors.Wrapf(err, "error reading container %s JSON ports", id) + } + if err := json.Unmarshal(fileContents, &ctr.config.PortMappings); err != nil { + return nil, errors.Wrapf(err, "error parsing container %s JSON ports", id) + } + } + return ctr, nil } diff --git a/libpod/sql_state_test.go b/libpod/sql_state_test.go index 020e2ce40..19d5da63e 100644 --- a/libpod/sql_state_test.go +++ b/libpod/sql_state_test.go @@ -3,6 +3,7 @@ package libpod import ( "encoding/json" "io/ioutil" + "net" "os" "path/filepath" "reflect" @@ -10,6 +11,7 @@ import ( "time" "github.com/containers/storage" + "github.com/cri-o/ocicni/pkg/ocicni" "github.com/opencontainers/runtime-tools/generate" "github.com/stretchr/testify/assert" ) @@ -29,6 +31,24 @@ func getTestContainer(id, name, locksDir string) (*Container, error) { StopSignal: 0, StopTimeout: 0, CreatedTime: time.Now(), + Privileged: true, + Mounts: []string{"/does/not/exist"}, + DNSServer: []net.IP{net.ParseIP("192.168.1.1"), net.ParseIP("192.168.2.2")}, + DNSSearch: []string{"example.com", "example.example.com"}, + PortMappings: []ocicni.PortMapping{ + { + HostPort: 80, + ContainerPort: 90, + Protocol: "tcp", + HostIP: "192.168.3.3", + }, + { + HostPort: 100, + ContainerPort: 110, + Protocol: "udp", + HostIP: "192.168.4.4", + }, + }, }, state: &containerRuntimeInfo{ State: ContainerStateRunning, |