windows: Rely on the OCI specs instead of custom type
Signed-off-by: Kenfe-Mickael Laventure <mickael.laventure@gmail.com>
This commit is contained in:
parent
bff040d087
commit
acf863a04e
@ -42,7 +42,7 @@ func newContainer(ctx context.Context, h *hcs.HCS, id string, spec *RuntimeSpec,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
hcsCtr, err := h.CreateContainer(ctx, id, spec.OCISpec, spec.Configuration, cio)
|
hcsCtr, err := h.CreateContainer(ctx, id, spec.OCISpec, spec.Configuration.TerminateDuration, cio)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -99,15 +99,15 @@ func (c *container) Start(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *container) Pause(ctx context.Context) error {
|
func (c *container) Pause(ctx context.Context) error {
|
||||||
if c.ctr.GetConfiguration().UseHyperV == false {
|
if c.ctr.IsHyperV() {
|
||||||
return fmt.Errorf("Windows non-HyperV containers do not support pause")
|
|
||||||
}
|
|
||||||
return c.ctr.Pause()
|
return c.ctr.Pause()
|
||||||
|
}
|
||||||
|
return errors.New("Windows non-HyperV containers do not support pause")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *container) Resume(ctx context.Context) error {
|
func (c *container) Resume(ctx context.Context) error {
|
||||||
if c.ctr.GetConfiguration().UseHyperV == false {
|
if c.ctr.IsHyperV() == false {
|
||||||
return fmt.Errorf("Windows non-HyperV containers do not support resume")
|
return errors.New("Windows non-HyperV containers do not support resume")
|
||||||
}
|
}
|
||||||
return c.ctr.Resume()
|
return c.ctr.Resume()
|
||||||
}
|
}
|
||||||
|
@ -58,18 +58,16 @@ func (s *HCS) LoadContainers(ctx context.Context) ([]*Container, error) {
|
|||||||
hcs: s,
|
hcs: s,
|
||||||
io: &IO{},
|
io: &IO{},
|
||||||
layerFolderPath: string(b),
|
layerFolderPath: string(b),
|
||||||
conf: Configuration{
|
terminateDuration: defaultTerminateTimeout,
|
||||||
TerminateDuration: defaultTerminateTimeout,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return containers, nil
|
return containers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(owner, rootDir string) *HCS {
|
func New(owner, root string) *HCS {
|
||||||
return &HCS{
|
return &HCS{
|
||||||
stateDir: rootDir,
|
stateDir: root,
|
||||||
owner: owner,
|
owner: owner,
|
||||||
pidPool: pid.NewPool(),
|
pidPool: pid.NewPool(),
|
||||||
}
|
}
|
||||||
@ -81,7 +79,7 @@ type HCS struct {
|
|||||||
pidPool *pid.Pool
|
pidPool *pid.Pool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *HCS) CreateContainer(ctx context.Context, id string, spec specs.Spec, conf Configuration, io *IO) (c *Container, err error) {
|
func (s *HCS) CreateContainer(ctx context.Context, id string, spec specs.Spec, terminateDuration time.Duration, io *IO) (c *Container, err error) {
|
||||||
pid, err := s.pidPool.Get()
|
pid, err := s.pidPool.Get()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -102,30 +100,30 @@ func (s *HCS) CreateContainer(ctx context.Context, id string, spec specs.Spec, c
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if conf.TerminateDuration == 0 {
|
if terminateDuration == 0 {
|
||||||
conf.TerminateDuration = defaultTerminateTimeout
|
terminateDuration = defaultTerminateTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
ctrConf, err := newContainerConfig(s.owner, id, spec, conf)
|
conf, err := newContainerConfig(ctx, s.owner, id, spec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
layerPathFile := filepath.Join(stateDir, layerFile)
|
layerPathFile := filepath.Join(stateDir, layerFile)
|
||||||
if err := ioutil.WriteFile(layerPathFile, []byte(ctrConf.LayerFolderPath), 0644); err != nil {
|
if err := ioutil.WriteFile(layerPathFile, []byte(conf.LayerFolderPath), 0644); err != nil {
|
||||||
log.G(ctx).WithError(err).Warnf("failed to save active layer %s", ctrConf.LayerFolderPath)
|
log.G(ctx).WithError(err).Warnf("failed to save active layer %s", conf.LayerFolderPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctr, err := hcsshim.CreateContainer(id, ctrConf)
|
ctr, err := hcsshim.CreateContainer(id, conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
removeLayer(ctx, ctrConf.LayerFolderPath)
|
removeLayer(ctx, conf.LayerFolderPath)
|
||||||
return nil, errors.Wrapf(err, "failed to create container %s", id)
|
return nil, errors.Wrapf(err, "failed to create container %s", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ctr.Start()
|
err = ctr.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctr.Terminate()
|
ctr.Terminate()
|
||||||
removeLayer(ctx, ctrConf.LayerFolderPath)
|
removeLayer(ctx, conf.LayerFolderPath)
|
||||||
return nil, errors.Wrapf(err, "failed to start container %s", id)
|
return nil, errors.Wrapf(err, "failed to start container %s", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,11 +132,11 @@ func (s *HCS) CreateContainer(ctx context.Context, id string, spec specs.Spec, c
|
|||||||
id: id,
|
id: id,
|
||||||
pid: pid,
|
pid: pid,
|
||||||
spec: spec,
|
spec: spec,
|
||||||
conf: conf,
|
|
||||||
stateDir: stateDir,
|
stateDir: stateDir,
|
||||||
io: io,
|
io: io,
|
||||||
hcs: s,
|
hcs: s,
|
||||||
layerFolderPath: ctrConf.LayerFolderPath,
|
conf: conf,
|
||||||
|
terminateDuration: terminateDuration,
|
||||||
processes: make([]*Process, 0),
|
processes: make([]*Process, 0),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@ -147,11 +145,13 @@ type Container struct {
|
|||||||
sync.Mutex
|
sync.Mutex
|
||||||
hcsshim.Container
|
hcsshim.Container
|
||||||
|
|
||||||
|
conf *hcsshim.ContainerConfig
|
||||||
|
terminateDuration time.Duration
|
||||||
|
|
||||||
id string
|
id string
|
||||||
stateDir string
|
stateDir string
|
||||||
pid uint32
|
pid uint32
|
||||||
spec specs.Spec
|
spec specs.Spec
|
||||||
conf Configuration
|
|
||||||
io *IO
|
io *IO
|
||||||
hcs *HCS
|
hcs *HCS
|
||||||
layerFolderPath string
|
layerFolderPath string
|
||||||
@ -179,7 +179,7 @@ func (c *Container) Start(ctx context.Context) error {
|
|||||||
func (c *Container) getDeathErr(err error) error {
|
func (c *Container) getDeathErr(err error) error {
|
||||||
switch {
|
switch {
|
||||||
case hcsshim.IsPending(err):
|
case hcsshim.IsPending(err):
|
||||||
err = c.WaitTimeout(c.conf.TerminateDuration)
|
err = c.WaitTimeout(c.terminateDuration)
|
||||||
case hcsshim.IsAlreadyStopped(err):
|
case hcsshim.IsAlreadyStopped(err):
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
@ -265,7 +265,7 @@ func (c *Container) Delete(ctx context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceCtr, err := c.hcs.CreateContainer(ctx, c.id+"_servicing", c.spec, c.conf, &IO{})
|
serviceCtr, err := c.hcs.CreateContainer(ctx, c.id+"_servicing", c.spec, c.terminateDuration, &IO{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.G(ctx).WithError(err).WithField("id", c.id).Warn("could not create servicing container")
|
log.G(ctx).WithError(err).WithField("id", c.id).Warn("could not create servicing container")
|
||||||
return
|
return
|
||||||
@ -299,8 +299,8 @@ func (c *Container) ExitCode() (uint32, error) {
|
|||||||
return c.processes[0].ExitCode()
|
return c.processes[0].ExitCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) GetConfiguration() Configuration {
|
func (c *Container) IsHyperV() bool {
|
||||||
return c.conf
|
return c.conf.HvPartition
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) AddProcess(ctx context.Context, id string, spec *specs.Process, io *IO) (*Process, error) {
|
func (c *Container) AddProcess(ctx context.Context, id string, spec *specs.Process, io *IO) (*Process, error) {
|
||||||
@ -416,30 +416,36 @@ func (c *Container) addProcess(ctx context.Context, id string, spec *specs.Proce
|
|||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// newHCSConfiguration generates a hcsshim configuration from the instance
|
// newContainerConfig generates a hcsshim container configuration from the
|
||||||
// OCI Spec and hcs.Configuration.
|
// provided OCI Spec
|
||||||
func newContainerConfig(owner, id string, spec specs.Spec, conf Configuration) (*hcsshim.ContainerConfig, error) {
|
func newContainerConfig(ctx context.Context, owner, id string, spec specs.Spec) (*hcsshim.ContainerConfig, error) {
|
||||||
configuration := &hcsshim.ContainerConfig{
|
if len(spec.Windows.LayerFolders) == 0 {
|
||||||
|
return nil, errors.New("LayerFolders cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
layerFolders = spec.Windows.LayerFolders
|
||||||
|
conf = &hcsshim.ContainerConfig{
|
||||||
SystemType: "Container",
|
SystemType: "Container",
|
||||||
Name: id,
|
Name: id,
|
||||||
Owner: owner,
|
Owner: owner,
|
||||||
HostName: spec.Hostname,
|
HostName: spec.Hostname,
|
||||||
IgnoreFlushesDuringBoot: conf.IgnoreFlushesDuringBoot,
|
IgnoreFlushesDuringBoot: spec.Windows.IgnoreFlushesDuringBoot,
|
||||||
HvPartition: conf.UseHyperV,
|
AllowUnqualifiedDNSQuery: spec.Windows.Network.AllowUnqualifiedDNSQuery,
|
||||||
AllowUnqualifiedDNSQuery: conf.AllowUnqualifiedDNSQuery,
|
EndpointList: spec.Windows.Network.EndpointList,
|
||||||
EndpointList: conf.NetworkEndpoints,
|
NetworkSharedContainerName: spec.Windows.Network.NetworkSharedContainerName,
|
||||||
NetworkSharedContainerName: conf.NetworkSharedContainerID,
|
Credentials: spec.Windows.CredentialSpec.(string),
|
||||||
Credentials: conf.Credentials,
|
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// TODO: use the create request Mount for those
|
// TODO: use the create request Mount for those
|
||||||
for _, layerPath := range conf.Layers {
|
for _, layerPath := range layerFolders {
|
||||||
_, filename := filepath.Split(layerPath)
|
_, filename := filepath.Split(layerPath)
|
||||||
guid, err := hcsshim.NameToGuid(filename)
|
guid, err := hcsshim.NameToGuid(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
configuration.Layers = append(configuration.Layers, hcsshim.Layer{
|
conf.Layers = append(conf.Layers, hcsshim.Layer{
|
||||||
ID: guid.ToString(),
|
ID: guid.ToString(),
|
||||||
Path: layerPath,
|
Path: layerPath,
|
||||||
})
|
})
|
||||||
@ -459,19 +465,20 @@ func newContainerConfig(owner, id string, spec specs.Spec, conf Configuration) (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
configuration.MappedDirectories = mds
|
conf.MappedDirectories = mds
|
||||||
}
|
}
|
||||||
|
|
||||||
if conf.DNSSearchList != nil {
|
if spec.Windows.Network.DNSSearchList != nil {
|
||||||
configuration.DNSSearchList = strings.Join(conf.DNSSearchList, ",")
|
conf.DNSSearchList = strings.Join(spec.Windows.Network.DNSSearchList, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
if configuration.HvPartition {
|
if spec.Windows.HyperV != nil {
|
||||||
for _, layerPath := range conf.Layers {
|
conf.HvPartition = true
|
||||||
utilityVMPath := filepath.Join(layerPath, "UtilityVM")
|
for _, layerPath := range layerFolders {
|
||||||
|
utilityVMPath := spec.Windows.HyperV.UtilityVMPath
|
||||||
_, err := os.Stat(utilityVMPath)
|
_, err := os.Stat(utilityVMPath)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
configuration.HvRuntime = &hcsshim.HvRuntime{ImagePath: utilityVMPath}
|
conf.HvRuntime = &hcsshim.HvRuntime{ImagePath: utilityVMPath}
|
||||||
break
|
break
|
||||||
} else if !os.IsNotExist(err) {
|
} else if !os.IsNotExist(err) {
|
||||||
return nil, errors.Wrapf(err, "failed to access layer %s", layerPath)
|
return nil, errors.Wrapf(err, "failed to access layer %s", layerPath)
|
||||||
@ -479,78 +486,71 @@ func newContainerConfig(owner, id string, spec specs.Spec, conf Configuration) (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(configuration.Layers) == 0 {
|
var (
|
||||||
// TODO: support starting with 0 layers, this mean we need the "filter" directory as parameter
|
err error
|
||||||
return nil, errors.New("at least one layers must be provided")
|
di = hcsshim.DriverInfo{
|
||||||
}
|
|
||||||
|
|
||||||
di := hcsshim.DriverInfo{
|
|
||||||
Flavour: 1, // filter driver
|
Flavour: 1, // filter driver
|
||||||
|
HomeDir: filepath.Dir(layerFolders[0]),
|
||||||
}
|
}
|
||||||
|
)
|
||||||
if len(configuration.Layers) > 0 {
|
|
||||||
di.HomeDir = filepath.Dir(conf.Layers[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Windows doesn't support creating a container with a readonly
|
// Windows doesn't support creating a container with a readonly
|
||||||
// filesystem, so always create a RW one
|
// filesystem, so always create a RW one
|
||||||
if err := hcsshim.CreateSandboxLayer(di, id, conf.Layers[0], conf.Layers); err != nil {
|
if err = hcsshim.CreateSandboxLayer(di, id, layerFolders[0], layerFolders); err != nil {
|
||||||
return nil, errors.Wrapf(err, "failed to create sandbox layer for %s: layers: %#v, driverInfo: %#v",
|
return nil, errors.Wrapf(err, "failed to create sandbox layer for %s: layers: %#v, driverInfo: %#v",
|
||||||
id, configuration.Layers, di)
|
id, layerFolders, di)
|
||||||
}
|
}
|
||||||
configuration.LayerFolderPath = filepath.Join(di.HomeDir, id)
|
conf.LayerFolderPath = filepath.Join(di.HomeDir, id)
|
||||||
|
defer func() {
|
||||||
err := hcsshim.ActivateLayer(di, id)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
removeLayer(context.TODO(), configuration.LayerFolderPath)
|
removeLayer(ctx, conf.LayerFolderPath)
|
||||||
return nil, errors.Wrapf(err, "failed to active layer %s", configuration.LayerFolderPath)
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err = hcsshim.ActivateLayer(di, id); err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "failed to activate layer %s", conf.LayerFolderPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = hcsshim.PrepareLayer(di, id, conf.Layers)
|
if err = hcsshim.PrepareLayer(di, id, layerFolders); err != nil {
|
||||||
if err != nil {
|
return nil, errors.Wrapf(err, "failed to prepare layer %s", conf.LayerFolderPath)
|
||||||
removeLayer(context.TODO(), configuration.LayerFolderPath)
|
|
||||||
return nil, errors.Wrapf(err, "failed to prepare layer %s", configuration.LayerFolderPath)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
volumePath, err := hcsshim.GetLayerMountPath(di, id)
|
conf.VolumePath, err = hcsshim.GetLayerMountPath(di, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err := hcsshim.DestroyLayer(di, id); err != nil {
|
|
||||||
log.L.Warnf("failed to DestroyLayer %s: %s", id, err)
|
|
||||||
}
|
|
||||||
return nil, errors.Wrapf(err, "failed to getmount path for layer %s: driverInfo: %#v", id, di)
|
return nil, errors.Wrapf(err, "failed to getmount path for layer %s: driverInfo: %#v", id, di)
|
||||||
}
|
}
|
||||||
configuration.VolumePath = volumePath
|
|
||||||
|
|
||||||
return configuration, nil
|
return conf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// removeLayer deletes the given layer, all associated containers must have
|
// removeLayer deletes the given layer, all associated containers must have
|
||||||
// been shutdown for this to succeed.
|
// been shutdown for this to succeed.
|
||||||
func removeLayer(ctx context.Context, path string) error {
|
func removeLayer(ctx context.Context, path string) error {
|
||||||
layerID := filepath.Base(path)
|
var (
|
||||||
parentPath := filepath.Dir(path)
|
err error
|
||||||
di := hcsshim.DriverInfo{
|
layerID = filepath.Base(path)
|
||||||
|
parentPath = filepath.Dir(path)
|
||||||
|
di = hcsshim.DriverInfo{
|
||||||
Flavour: 1, // filter driver
|
Flavour: 1, // filter driver
|
||||||
HomeDir: parentPath,
|
HomeDir: parentPath,
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
err := hcsshim.UnprepareLayer(di, layerID)
|
if err = hcsshim.UnprepareLayer(di, layerID); err != nil {
|
||||||
if err != nil {
|
|
||||||
log.G(ctx).WithError(err).Warnf("failed to unprepare layer %s for removal", path)
|
log.G(ctx).WithError(err).Warnf("failed to unprepare layer %s for removal", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = hcsshim.DeactivateLayer(di, layerID)
|
if err = hcsshim.DeactivateLayer(di, layerID); err != nil {
|
||||||
if err != nil {
|
|
||||||
log.G(ctx).WithError(err).Warnf("failed to deactivate layer %s for removal", path)
|
log.G(ctx).WithError(err).Warnf("failed to deactivate layer %s for removal", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
removePath := filepath.Join(parentPath, fmt.Sprintf("%s-removing", layerID))
|
removePath := filepath.Join(parentPath, fmt.Sprintf("%s-removing", layerID))
|
||||||
err = os.Rename(path, removePath)
|
if err = os.Rename(path, removePath); err != nil {
|
||||||
if err != nil {
|
|
||||||
log.G(ctx).WithError(err).Warnf("failed to rename container layer %s for removal", path)
|
log.G(ctx).WithError(err).Warnf("failed to rename container layer %s for removal", path)
|
||||||
removePath = path
|
removePath = path
|
||||||
}
|
}
|
||||||
if err := hcsshim.DestroyLayer(di, removePath); err != nil {
|
|
||||||
|
if err = hcsshim.DestroyLayer(di, removePath); err != nil {
|
||||||
log.G(ctx).WithError(err).Errorf("failed to remove container layer %s", removePath)
|
log.G(ctx).WithError(err).Errorf("failed to remove container layer %s", removePath)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -5,18 +5,5 @@ package hcs
|
|||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
type Configuration struct {
|
type Configuration struct {
|
||||||
UseHyperV bool `json:"useHyperV,omitempty"`
|
|
||||||
|
|
||||||
Layers []string `json:"layers"`
|
|
||||||
|
|
||||||
TerminateDuration time.Duration `json:"terminateDuration,omitempty"`
|
TerminateDuration time.Duration `json:"terminateDuration,omitempty"`
|
||||||
|
|
||||||
IgnoreFlushesDuringBoot bool `json:"ignoreFlushesDuringBoot,omitempty"`
|
|
||||||
|
|
||||||
AllowUnqualifiedDNSQuery bool `json:"allowUnqualifiedDNSQuery,omitempty"`
|
|
||||||
DNSSearchList []string `json:"dnsSearchList,omitempty"`
|
|
||||||
NetworkEndpoints []string `json:"networkEndpoints,omitempty"`
|
|
||||||
NetworkSharedContainerID string
|
|
||||||
|
|
||||||
Credentials string `json:"credentials,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user