diff options
Diffstat (limited to 'libpod/boltdb_state.go')
-rw-r--r-- | libpod/boltdb_state.go | 363 |
1 files changed, 359 insertions, 4 deletions
diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go index 9dd5ca465..dcb2ff751 100644 --- a/libpod/boltdb_state.go +++ b/libpod/boltdb_state.go @@ -50,10 +50,12 @@ type BoltState struct { // containers in the pod. // - allPodsBkt: Map of ID to name containing only pods. Used for pod lookup // operations. -// - execBkt: Map of exec session ID to exec session - contains a sub-bucket for -// each exec session in the DB. -// - execRegistryBkt: Map of exec session ID to nothing. Contains one entry for -// each exec session. Used for iterating through all exec sessions. +// - execBkt: Map of exec session ID to container ID - used for resolving +// exec session IDs to the containers that hold the exec session. +// - aliasesBkt - Contains a bucket for each CNI network, which contain a map of +// network alias (an extra name for containers in DNS) to the ID of the +// container holding the alias. Aliases must be unique per-network, and cannot +// conflict with names registered in nameRegistryBkt. // - runtimeConfigBkt: Contains configuration of the libpod instance that // initially created the database. This must match for any further instances // that access the database, to ensure that state mismatches with @@ -969,6 +971,359 @@ func (s *BoltState) AllContainers() ([]*Container, error) { return ctrs, nil } +// GetNetworks returns the CNI networks this container is a part of. +func (s *BoltState) GetNetworks(ctr *Container) ([]string, error) { + if !s.valid { + return nil, define.ErrDBClosed + } + + if !ctr.valid { + return nil, define.ErrCtrRemoved + } + + if s.namespace != "" && s.namespace != ctr.config.Namespace { + return nil, errors.Wrapf(define.ErrNSMismatch, "container %s is in namespace %q, does not match our namespace %q", ctr.ID(), ctr.config.Namespace, s.namespace) + } + + ctrID := []byte(ctr.ID()) + + db, err := s.getDBCon() + if err != nil { + return nil, err + } + defer s.deferredCloseDBCon(db) + + networks := []string{} + + err = db.View(func(tx *bolt.Tx) error { + ctrBucket, err := getCtrBucket(tx) + if err != nil { + return err + } + + dbCtr := ctrBucket.Bucket(ctrID) + if dbCtr == nil { + ctr.valid = false + return errors.Wrapf(define.ErrNoSuchCtr, "container %s does not exist in database", ctr.ID()) + } + + ctrNetworkBkt := dbCtr.Bucket(networksBkt) + if ctrNetworkBkt == nil { + return errors.Wrapf(define.ErrNoSuchNetwork, "container %s is not joined to any CNI networks", ctr.ID()) + } + + return ctrNetworkBkt.ForEach(func(network, v []byte) error { + networks = append(networks, string(network)) + return nil + }) + }) + if err != nil { + return nil, err + } + return networks, nil +} + +// GetNetworkAliases retrieves the network aliases for the given container in +// the given CNI network. +func (s *BoltState) GetNetworkAliases(ctr *Container, network string) ([]string, error) { + if !s.valid { + return nil, define.ErrDBClosed + } + + if !ctr.valid { + return nil, define.ErrCtrRemoved + } + + if network == "" { + return nil, errors.Wrapf(define.ErrInvalidArg, "network names must not be empty") + } + + if s.namespace != "" && s.namespace != ctr.config.Namespace { + return nil, errors.Wrapf(define.ErrNSMismatch, "container %s is in namespace %q, does not match our namespace %q", ctr.ID(), ctr.config.Namespace, s.namespace) + } + + ctrID := []byte(ctr.ID()) + + db, err := s.getDBCon() + if err != nil { + return nil, err + } + defer s.deferredCloseDBCon(db) + + aliases := []string{} + + err = db.View(func(tx *bolt.Tx) error { + ctrBucket, err := getCtrBucket(tx) + if err != nil { + return err + } + + dbCtr := ctrBucket.Bucket(ctrID) + if dbCtr == nil { + ctr.valid = false + return errors.Wrapf(define.ErrNoSuchCtr, "container %s does not exist in database", ctr.ID()) + } + + ctrNetworkBkt := dbCtr.Bucket(networksBkt) + if ctrNetworkBkt == nil { + // No networks joined, so no aliases + return nil + } + + inNetwork := ctrNetworkBkt.Get([]byte(network)) + if inNetwork == nil { + return errors.Wrapf(define.ErrNoAliases, "container %s is not part of network %s, no aliases found", ctr.ID(), network) + } + + ctrAliasesBkt := dbCtr.Bucket(aliasesBkt) + if ctrAliasesBkt == nil { + // No aliases + return nil + } + + netAliasesBkt := ctrAliasesBkt.Bucket([]byte(network)) + if netAliasesBkt == nil { + // No aliases for this specific network. + return nil + } + + return netAliasesBkt.ForEach(func(alias, v []byte) error { + aliases = append(aliases, string(alias)) + return nil + }) + }) + if err != nil { + return nil, err + } + + return aliases, nil +} + +// GetAllNetworkAliases retrieves the network aliases for the given container in +// all CNI networks. +func (s *BoltState) GetAllNetworkAliases(ctr *Container) (map[string][]string, error) { + if !s.valid { + return nil, define.ErrDBClosed + } + + if !ctr.valid { + return nil, define.ErrCtrRemoved + } + + if s.namespace != "" && s.namespace != ctr.config.Namespace { + return nil, errors.Wrapf(define.ErrNSMismatch, "container %s is in namespace %q, does not match our namespace %q", ctr.ID(), ctr.config.Namespace, s.namespace) + } + + ctrID := []byte(ctr.ID()) + + db, err := s.getDBCon() + if err != nil { + return nil, err + } + defer s.deferredCloseDBCon(db) + + aliases := make(map[string][]string) + + err = db.View(func(tx *bolt.Tx) error { + ctrBucket, err := getCtrBucket(tx) + if err != nil { + return err + } + + dbCtr := ctrBucket.Bucket(ctrID) + if dbCtr == nil { + ctr.valid = false + return errors.Wrapf(define.ErrNoSuchCtr, "container %s does not exist in database", ctr.ID()) + } + + ctrAliasesBkt := dbCtr.Bucket(aliasesBkt) + if ctrAliasesBkt == nil { + // No aliases present + return nil + } + + ctrNetworkBkt := dbCtr.Bucket(networksBkt) + if ctrNetworkBkt == nil { + // No networks joined, so no aliases + return nil + } + + return ctrNetworkBkt.ForEach(func(network, v []byte) error { + netAliasesBkt := ctrAliasesBkt.Bucket(network) + if netAliasesBkt == nil { + return nil + } + + netAliases := []string{} + + _ = netAliasesBkt.ForEach(func(alias, v []byte) error { + netAliases = append(netAliases, string(alias)) + return nil + }) + + aliases[string(network)] = netAliases + return nil + }) + }) + if err != nil { + return nil, err + } + + return aliases, nil +} + +// NetworkConnect adds the given container to the given network. If aliases are +// specified, those will be added to the given network. +func (s *BoltState) NetworkConnect(ctr *Container, network string, aliases []string) error { + if !s.valid { + return define.ErrDBClosed + } + + if !ctr.valid { + return define.ErrCtrRemoved + } + + if network == "" { + return errors.Wrapf(define.ErrInvalidArg, "network names must not be empty") + } + + if s.namespace != "" && s.namespace != ctr.config.Namespace { + return errors.Wrapf(define.ErrNSMismatch, "container %s is in namespace %q, does not match our namespace %q", ctr.ID(), ctr.config.Namespace, s.namespace) + } + + ctrID := []byte(ctr.ID()) + + db, err := s.getDBCon() + if err != nil { + return err + } + defer s.deferredCloseDBCon(db) + + return db.Update(func(tx *bolt.Tx) error { + ctrBucket, err := getCtrBucket(tx) + if err != nil { + return err + } + + dbCtr := ctrBucket.Bucket(ctrID) + if dbCtr == nil { + ctr.valid = false + return errors.Wrapf(define.ErrNoSuchCtr, "container %s does not exist in database", ctr.ID()) + } + + ctrAliasesBkt, err := dbCtr.CreateBucketIfNotExists(aliasesBkt) + if err != nil { + return errors.Wrapf(err, "error creating aliases bucket for container %s", ctr.ID()) + } + + ctrNetworksBkt := dbCtr.Bucket(networksBkt) + if ctrNetworksBkt == nil { + ctrNetworksBkt, err = dbCtr.CreateBucket(networksBkt) + if err != nil { + return errors.Wrapf(err, "error creating networks bucket for container %s", ctr.ID()) + } + ctrNetworks := ctr.config.Networks + if len(ctrNetworks) == 0 { + ctrNetworks = []string{ctr.runtime.netPlugin.GetDefaultNetworkName()} + } + // Copy in all the container's CNI networks + for _, net := range ctrNetworks { + if err := ctrNetworksBkt.Put([]byte(net), ctrID); err != nil { + return errors.Wrapf(err, "error adding container %s network %s to DB", ctr.ID(), net) + } + } + } + netConnected := ctrNetworksBkt.Get([]byte(network)) + if netConnected != nil { + return errors.Wrapf(define.ErrNetworkExists, "container %s is already connected to CNI network %q", ctr.ID(), network) + } + + // Add the network + if err := ctrNetworksBkt.Put([]byte(network), ctrID); err != nil { + return errors.Wrapf(err, "error adding container %s to network %s in DB", ctr.ID(), network) + } + + ctrNetAliasesBkt, err := ctrAliasesBkt.CreateBucketIfNotExists([]byte(network)) + if err != nil { + return errors.Wrapf(err, "error adding container %s network aliases bucket for network %s", ctr.ID(), network) + } + for _, alias := range aliases { + if err := ctrNetAliasesBkt.Put([]byte(alias), ctrID); err != nil { + return errors.Wrapf(err, "error adding container %s network alias %s for network %s", ctr.ID(), alias, network) + } + } + return nil + }) +} + +// NetworkDisconnect disconnects the container from the given network, also +// removing any aliases in the network. +func (s *BoltState) NetworkDisconnect(ctr *Container, network string) error { + if !s.valid { + return define.ErrDBClosed + } + + if !ctr.valid { + return define.ErrCtrRemoved + } + + if network == "" { + return errors.Wrapf(define.ErrInvalidArg, "network names must not be empty") + } + + if s.namespace != "" && s.namespace != ctr.config.Namespace { + return errors.Wrapf(define.ErrNSMismatch, "container %s is in namespace %q, does not match our namespace %q", ctr.ID(), ctr.config.Namespace, s.namespace) + } + + ctrID := []byte(ctr.ID()) + + db, err := s.getDBCon() + if err != nil { + return err + } + defer s.deferredCloseDBCon(db) + + return db.Update(func(tx *bolt.Tx) error { + ctrBucket, err := getCtrBucket(tx) + if err != nil { + return err + } + + dbCtr := ctrBucket.Bucket(ctrID) + if dbCtr == nil { + ctr.valid = false + return errors.Wrapf(define.ErrNoSuchCtr, "container %s does not exist in database", ctr.ID()) + } + + ctrAliasesBkt := dbCtr.Bucket(aliasesBkt) + ctrNetworksBkt := dbCtr.Bucket(networksBkt) + if ctrNetworksBkt == nil { + return errors.Wrapf(define.ErrNoSuchNetwork, "container %s is not connected to any CNI networks, so cannot disconnect", ctr.ID()) + } + netConnected := ctrNetworksBkt.Get([]byte(network)) + if netConnected == nil { + return errors.Wrapf(define.ErrNoSuchNetwork, "container %s is not connected to CNI network %q", ctr.ID(), network) + } + + if err := ctrNetworksBkt.Delete([]byte(network)); err != nil { + return errors.Wrapf(err, "error removing container %s from network %s", ctr.ID(), network) + } + + if ctrAliasesBkt != nil { + bktExists := ctrAliasesBkt.Bucket([]byte(network)) + if bktExists == nil { + return nil + } + + if err := ctrAliasesBkt.DeleteBucket([]byte(network)); err != nil { + return errors.Wrapf(err, "error removing container %s network aliases for network %s", ctr.ID(), network) + } + } + + return nil + }) +} + // GetContainerConfig returns a container config from the database by full ID func (s *BoltState) GetContainerConfig(id string) (*ContainerConfig, error) { if len(id) == 0 { |