|
|
|
@ -9,12 +9,11 @@ import (
|
|
|
|
|
"path" |
|
|
|
|
"time" |
|
|
|
|
|
|
|
|
|
"git.wegmueller.it/illumos/go-zone" |
|
|
|
|
"git.wegmueller.it/illumos/go-zone/config" |
|
|
|
|
"git.wegmueller.it/illumos/go-zone/lifecycle" |
|
|
|
|
iconf "git.wegmueller.it/opencloud/opencloud/config" |
|
|
|
|
"git.wegmueller.it/opencloud/opencloud/image" |
|
|
|
|
"git.wegmueller.it/opencloud/opencloud/image/reference" |
|
|
|
|
"git.wegmueller.it/opencloud/opencloud/pod/meta" |
|
|
|
|
"git.wegmueller.it/opencloud/opencloud/pod/runner" |
|
|
|
|
"git.wegmueller.it/opencloud/opencloud/volume" |
|
|
|
|
"git.wegmueller.it/opencloud/opencloud/zfs" |
|
|
|
|
imageSpec "github.com/opencontainers/image-spec/specs-go/v1" |
|
|
|
@ -25,43 +24,35 @@ import (
|
|
|
|
|
|
|
|
|
|
const ( |
|
|
|
|
ROOTFSDatasetName = "root" |
|
|
|
|
BrandBHyve = "bhyve" |
|
|
|
|
BrandKVM = "kvm" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
type Container struct { |
|
|
|
|
status Status |
|
|
|
|
status meta.Status |
|
|
|
|
dataset *zfs.Dataset |
|
|
|
|
rootDS *zfs.Dataset |
|
|
|
|
Name string `json:"name"` |
|
|
|
|
UUID uuid.UUID `json:"uuid"` |
|
|
|
|
Manifest *ContainerManifest `json:"manifest"` |
|
|
|
|
Image image.Image `json:"image"` |
|
|
|
|
ImageReference reference.Reference `json:"image_reference"` |
|
|
|
|
Zone *config.Zone `json:"zone,omitempty"` |
|
|
|
|
TopLayerDescriptor imageSpec.Descriptor `json:"image_layer_desc"` |
|
|
|
|
Name string `json:"name"` |
|
|
|
|
UUID uuid.UUID `json:"uuid"` |
|
|
|
|
Manifest *meta.ContainerManifest `json:"manifest"` |
|
|
|
|
Image image.Image `json:"image"` |
|
|
|
|
ImageReference reference.Reference `json:"image_reference"` |
|
|
|
|
TopLayerDescriptor imageSpec.Descriptor `json:"image_layer_desc"` |
|
|
|
|
runner runner.Runner |
|
|
|
|
volumes map[string]*volume.Volume |
|
|
|
|
lifecycleManager lifecycle.Lifecyclemanager |
|
|
|
|
sealed bool |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func newContainer(manifest *ContainerManifest, name string) *Container { |
|
|
|
|
c := &Container{ |
|
|
|
|
Name: name, |
|
|
|
|
UUID: uuid.NewV4(), |
|
|
|
|
Manifest: manifest, |
|
|
|
|
volumes: make(map[string]*volume.Volume), |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return c |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (c *Container) GetDataset() *zfs.Dataset { |
|
|
|
|
return c.dataset |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func CreateEmptyContainer(parentDataset *zfs.Dataset, m *ContainerManifest, name string) (container *Container, rErr error) { |
|
|
|
|
container = newContainer(m, name) |
|
|
|
|
func CreateEmptyContainer(parentDataset *zfs.Dataset, m *meta.ContainerManifest, name string) (container *Container, rErr error) { |
|
|
|
|
container = &Container{ |
|
|
|
|
Name: name, |
|
|
|
|
UUID: uuid.NewV4(), |
|
|
|
|
Manifest: m, |
|
|
|
|
volumes: make(map[string]*volume.Volume), |
|
|
|
|
runner: &runner.ZoneRunner{}, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if err := container.initDataset(parentDataset); err != nil { |
|
|
|
|
return nil, tracerr.Wrap(err) |
|
|
|
@ -82,11 +73,12 @@ func CreateEmptyContainer(parentDataset *zfs.Dataset, m *ContainerManifest, name
|
|
|
|
|
return nil, tracerr.Wrap(err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if err := container.initNewZone(); err != nil { |
|
|
|
|
return nil, tracerr.Wrap(err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if err := container.register(); err != nil { |
|
|
|
|
if err := container.runner.CreateContainer(runner.CreateOpts{ |
|
|
|
|
UUID: container.UUID, |
|
|
|
|
Name: container.Name, |
|
|
|
|
Path: container.Path(), |
|
|
|
|
Manifest: m, |
|
|
|
|
}); err != nil { |
|
|
|
|
return nil, tracerr.Wrap(err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -97,7 +89,7 @@ func CreateEmptyContainer(parentDataset *zfs.Dataset, m *ContainerManifest, name
|
|
|
|
|
return container, nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func CreateContainer(parentDataset *zfs.Dataset, m *ContainerManifest, name, tag string, repo *image.Repository) (container *Container, rErr error) { |
|
|
|
|
func CreateContainer(parentDataset *zfs.Dataset, m *meta.ContainerManifest, name, tag string, repo *image.Repository) (container *Container, rErr error) { |
|
|
|
|
|
|
|
|
|
container = &Container{ |
|
|
|
|
UUID: uuid.NewV4(), |
|
|
|
@ -129,15 +121,16 @@ func CreateContainer(parentDataset *zfs.Dataset, m *ContainerManifest, name, tag
|
|
|
|
|
return nil, tracerr.Wrap(err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if err := container.initNewZone(); err != nil { |
|
|
|
|
return nil, tracerr.Wrap(err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
container.Manifest.Spec.Root = &spec.Root{ |
|
|
|
|
Path: container.rootDS.Mountpoint, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if err := container.register(); err != nil { |
|
|
|
|
if err := container.runner.CreateContainer(runner.CreateOpts{ |
|
|
|
|
UUID: container.UUID, |
|
|
|
|
Name: container.Name, |
|
|
|
|
Path: container.Path(), |
|
|
|
|
Manifest: m, |
|
|
|
|
}); err != nil { |
|
|
|
|
return nil, tracerr.Wrap(err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -202,57 +195,7 @@ func (c *Container) cloneImageToContainer(repo *image.Repository) error {
|
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (c *Container) initNewZone() (err error) { |
|
|
|
|
c.Zone = newZoneFromContainer(c) |
|
|
|
|
|
|
|
|
|
c.lifecycleManager, err = lifecycle.NewManager(c.Zone) |
|
|
|
|
if err != nil { |
|
|
|
|
return tracerr.Wrap(err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if err := os.Chmod(c.dataset.Mountpoint, 0700); err != nil { |
|
|
|
|
return tracerr.Wrap(err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (c *Container) register() (err error) { |
|
|
|
|
var lockFd *os.File |
|
|
|
|
if lockFd, err = config.GrabZoneLockFile(*c.Zone); err != nil { |
|
|
|
|
return tracerr.Wrap(err) |
|
|
|
|
} |
|
|
|
|
defer config.ReleaseZoneLockFile(lockFd) |
|
|
|
|
if err = c.Zone.WriteToFile(); err != nil { |
|
|
|
|
return tracerr.Wrap(err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if err = config.Register(c.Zone); err != nil { |
|
|
|
|
return tracerr.Wrap(err) |
|
|
|
|
} |
|
|
|
|
manifestJSON, err := json.Marshal(c) |
|
|
|
|
if err != nil { |
|
|
|
|
return tracerr.Wrap(err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if err := ioutil.WriteFile(c.Path("pod.json"), manifestJSON, 0440); err != nil { |
|
|
|
|
return tracerr.Wrap(err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if c.Zone.Brand != DefaultPodBrandName { |
|
|
|
|
if err = c.lifecycleManager.Install(nil); err != nil { |
|
|
|
|
return tracerr.Wrap(err) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
c.sealed = true |
|
|
|
|
c.status = StatusStopped |
|
|
|
|
|
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (c *Container) initVolumes() error { |
|
|
|
|
|
|
|
|
|
for i, volCfg := range c.Manifest.Volumes { |
|
|
|
|
if volCfg.Name == "" { |
|
|
|
|
volCfg.Name = fmt.Sprintf("volume.%d", i) |
|
|
|
@ -313,6 +256,7 @@ func LoadContainer(parentDS *zfs.Dataset, id uuid.UUID) (*Container, error) {
|
|
|
|
|
if id == uuid.Nil { |
|
|
|
|
panic("No UUID provided") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
containerDS, err := parentDS.GetChildDataset(path.Join("pods", id.String())) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, tracerr.Wrap(err) |
|
|
|
@ -322,6 +266,9 @@ func LoadContainer(parentDS *zfs.Dataset, id uuid.UUID) (*Container, error) {
|
|
|
|
|
dataset: containerDS, |
|
|
|
|
UUID: id, |
|
|
|
|
volumes: make(map[string]*volume.Volume), |
|
|
|
|
runner: &runner.ZoneRunner{ |
|
|
|
|
UUID: id, |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if err := container.Load(); err != nil { |
|
|
|
@ -366,19 +313,6 @@ func (c *Container) Load() error {
|
|
|
|
|
return tracerr.Wrap(err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if c.Zone == nil { |
|
|
|
|
c.Zone = config.New(c.UUID.String()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if err := c.Zone.ReadFromFile(); err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
c.lifecycleManager, err = lifecycle.NewManager(c.Zone) |
|
|
|
|
if err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for _, child := range c.dataset.Children { |
|
|
|
|
if child.GetName() == "root" { |
|
|
|
|
continue |
|
|
|
@ -392,6 +326,12 @@ func (c *Container) Load() error {
|
|
|
|
|
c.volumes[vol.GetDatasetMountPoint()] = vol |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
c.runner = &runner.ZoneRunner{ |
|
|
|
|
UUID: c.UUID, |
|
|
|
|
Name: c.Name, |
|
|
|
|
ZonePath: c.Path(), |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
c.sealed = true |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
@ -414,20 +354,11 @@ func (c *Container) Save() error {
|
|
|
|
|
return tracerr.Wrap(err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if err = c.Zone.WriteToFile(); err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (c *Container) Status() Status { |
|
|
|
|
if zone.GetZoneIdByName(c.UUID.String()) == -1 { |
|
|
|
|
c.status = StatusStopped |
|
|
|
|
} else { |
|
|
|
|
c.status = StatusRunning |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (c *Container) Status() meta.Status { |
|
|
|
|
c.status = c.runner.Status() |
|
|
|
|
return c.status |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -436,19 +367,19 @@ func (c *Container) Kill() error {
|
|
|
|
|
panic("container not properly loaded, hit the developer of the app who forgot to use the c.Load() function") |
|
|
|
|
} |
|
|
|
|
switch status := c.Status(); status { |
|
|
|
|
case StatusStopped: |
|
|
|
|
case meta.StatusStopped: |
|
|
|
|
// All's fine
|
|
|
|
|
return nil |
|
|
|
|
case StatusRunning: |
|
|
|
|
c.status = StatusDying |
|
|
|
|
case meta.StatusRunning: |
|
|
|
|
c.status = meta.StatusDying |
|
|
|
|
var err error |
|
|
|
|
if err = c.lifecycleManager.Shutdown(nil); err != nil { |
|
|
|
|
// Try a second time with halt
|
|
|
|
|
if err = c.lifecycleManager.Halt(); err != nil { |
|
|
|
|
if err = c.runner.StopContainer(false); err != nil { |
|
|
|
|
// Try a second time with halt (force)
|
|
|
|
|
if err = c.runner.StopContainer(true); err != nil { |
|
|
|
|
return tracerr.Wrap(err) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
case StatusDying: |
|
|
|
|
case meta.StatusDying: |
|
|
|
|
time.Sleep(250 * time.Millisecond) |
|
|
|
|
return nil |
|
|
|
|
default: |
|
|
|
@ -462,35 +393,16 @@ func (c *Container) Destroy() error {
|
|
|
|
|
panic("conatiner not properly loaded, hit the developer of the app who forgot to use the c.Load() function") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if status := c.Status(); status == StatusRunning { |
|
|
|
|
if status := c.Status(); status == meta.StatusRunning { |
|
|
|
|
if err := c.Kill(); err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if err := c.lifecycleManager.Uninstall(true, nil); err != nil { |
|
|
|
|
return fmt.Errorf("can not uninstall zone %s: %w", c.UUID, err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if err := c.dataset.Destroy(true); err != nil { |
|
|
|
|
return fmt.Errorf("cannot destroy pod dataset for %s: %w", c.UUID, err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var lockFd *os.File |
|
|
|
|
var err error |
|
|
|
|
if lockFd, err = config.GrabZoneLockFile(*c.Zone); err != nil { |
|
|
|
|
return tracerr.Wrap(err) |
|
|
|
|
} |
|
|
|
|
defer config.ReleaseZoneLockFile(lockFd) |
|
|
|
|
|
|
|
|
|
if err := config.Unregister(c.Zone); err != nil { |
|
|
|
|
return fmt.Errorf("cannot unregister zone of %s: %w", c.UUID, err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if err := c.Zone.RemoveFile(); err != nil { |
|
|
|
|
return fmt.Errorf("can not remove zone xml of %s: %w", c.UUID, err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if err := os.Remove(c.Path()); err != nil { |
|
|
|
|
if !os.IsNotExist(err) { |
|
|
|
|
return fmt.Errorf("error during path cleanup of %s: %w", c.UUID, err) |
|
|
|
@ -503,9 +415,19 @@ func (c *Container) Destroy() error {
|
|
|
|
|
// Starts the Zone
|
|
|
|
|
// Only to be called after the zone setup has been done
|
|
|
|
|
func (c *Container) Run() error { |
|
|
|
|
if c.lifecycleManager == nil || !c.sealed { |
|
|
|
|
if c.runner == nil || !c.sealed { |
|
|
|
|
return fmt.Errorf("container %s not properly loaded cannot start", c.Name) |
|
|
|
|
} |
|
|
|
|
c.status = StatusRunning |
|
|
|
|
return c.lifecycleManager.Boot(nil) |
|
|
|
|
|
|
|
|
|
if c.Status() != meta.StatusStopped { |
|
|
|
|
return fmt.Errorf("can only start stopped containers") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if err := c.runner.StartContainer(); err != nil { |
|
|
|
|
return tracerr.Wrap(err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
c.status = meta.StatusRunning |
|
|
|
|
|
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|